import gql from 'graphql-tag';
import { Store } from 'vuex';
import { ActionTree, MutationTree } from 'vuex';
import { Optional } from '~/typings/helpers';
import { Config, Profile } from '~/typings/models';
import refreshProfileQuery from '~/assets/query/store/refreshProfile.gql';
import authenticateQuery from '~/assets/query/store/authenticate.gql';
import displayTaskQuery from '~/assets/query/store/displayTask.gql';
import fetchConfigQuery from '~/assets/query/store/fetchConfig.gql';
import signInQuery from '~/assets/query/store/signIn.gql';
import signUpQuery from '~/assets/query/store/signUp.gql';

export const state = () => ({
	profile: undefined as Optional<Profile>,
	language: 'en-US',
	isAuthed: false,
	isDark: false,
	taskView: null,
	taskViewOpen: false,
	config: {
		instanceName: '',
		maintenance: false,
		allowAnonymous: false,
		allowRegister: false,
		verification: false
	} as Config
});

export type RootState = ReturnType<typeof state>;

export const mutations: MutationTree<RootState> = {

	/**
	 * Save the config instance
	 */
	storeConfig(state, config) {
		state.config = config;
	},

	/**
	 * Store the current user profile
	 */
	storeProfile(state, profile) {
		state.profile = profile;
	},

	/**
	 * Set the authenticated status
	 */
	setAuthStatus(state, value) {
		state.isAuthed = value;

		if(value && state.config.installing) {
			state.config.installing = false;
		}
	},

	/**
	 * Set the current theme
	 */
	setDarkMode(this: Store<any>, state, value) {
		this.$cookies.set('theme', value ? 'dark' : 'light');
		state.isDark = value;
	},

	/**
	 * Set the currently loaded interface language
	 */
	setLanguage(state, language) {
		localStorage.setItem('vindigo:lang', language);
		state.language = language;
	},

	/**
	 * Open the task view with no task inside
	 */
	openTaskView(state) {
		state.taskView = null;
		state.taskViewOpen = true;
	},

	/**
	 * Set the currently displayed task
	 */
	setCurrentTask(state, task) {
		state.taskView = task;
		state.taskViewOpen = true;
	},

	/**
	 * Hide the currently displayed task
	 */
	hideCurrentTask(state) {
		state.taskViewOpen = false;
	}

};

export const actions: ActionTree<RootState, RootState> = {
	
	/**
	 * Initialize the application
	 */
	async nuxtServerInit({ dispatch, commit }, { $cookies }) {
		const currentTheme = $cookies.get('theme');

		commit('setDarkMode', currentTheme =='dark');

		await Promise.all([
			dispatch('fetchConfig'),
			dispatch('authenticate'),
		]);
	},
	
	/**
	 * Request the latest client config
	 */
	async fetchConfig({ commit }) {
		const { config } = await this.$api.query(fetchConfigQuery);

		commit('storeConfig', config);
	},

	/**
	 * Authenticate with the server
	 */
	async authenticate({ commit }) {
		const res = await this.$api.query(authenticateQuery);

		commit('storeProfile', res.profile);
		commit('setAuthStatus', !!res.profile);
	},

	/**
	 * Sign in to a valid user account
	 */
	async signIn({ commit }, details) {
		const result = await this.$api.query(signInQuery, { details });

		commit('storeProfile', result.authenticate);
		commit('setAuthStatus', !!result.authenticate);

		return result.authenticate;
	},

	/**
	 * Sign up using the specified registration details
	 */
	async signUp({ commit }, details) {
		const result = await this.$api.query(signUpQuery, { details });

		commit('storeProfile', result.register);
		commit('setAuthStatus', !!result.register);

		return result.register;
	},

	/**
	 * Sign the user out from the application
	 */
	async signOut(this: Store<any>, { commit }) {
		this.$api.query(gql`
			mutation {
				signOut
			}
		`);

		commit('setAuthStatus', false);
		
		this.$router.push('/authenticate');
	},

	/**
	 * Refresh the current users profile
	 */
	async updateProfile({ commit }) {
		const result = await this.$api.query(refreshProfileQuery);

		commit('storeProfile', result.profile);
	},

	/**
	 * Display the given task in the task view
	 */
	async openTaskView({ commit }, id) {
		commit('openTaskView');

		const result = await this.$api.query(displayTaskQuery, {
			id: id
		});

		if(result.task) {
			commit('setCurrentTask', result.task);
		} else {
			commit('hideCurrentTask');
		}
	}

};