import { defineStore } from 'pinia';
import { ApolloQueryResult, FetchResult } from '@apollo/client/core';

import { LocalStorage } from 'quasar';
import { SessionStorage } from 'quasar';
import {
  addUniversoUzanto,
  login_query,
  logout_query,
} from 'src/queries/mutations';
import { listOrganizaiton, mi } from 'src/queries/queries';

import { apollo } from 'src/boot/apollo';
import { debugLog } from 'src/utils';
import { Mi, MiWrapper, CurrentUserState } from 'src/types/stores/current-user';

// Утилиты для работы с хранилищем
function getStorageItem(key: string): string | null {
  return SessionStorage.has(key)
    ? SessionStorage.getItem(key)
    : LocalStorage.getItem(key);
}

function setStorageItem(
  key: string,
  value: string,
  saveInLocalStorage: boolean,
): void {
  if (saveInLocalStorage) {
    LocalStorage.set(key, value);
  } else {
    SessionStorage.set(key, value);
  }
}

function clearStorage(keys: string[]): void {
  keys.forEach((key) => {
    LocalStorage.remove(key);
    SessionStorage.remove(key);
  });
}

// Универсальный метод для работы с Apollo
async function executeApolloOperation<T>({
  isMutation,
  query,
  variables,
  message,
}: {
  isMutation: boolean;
  query: any;
  variables?: Record<string, any>;
  message?: string;
}): Promise<ApolloQueryResult<T> | FetchResult<T>> {
  if (message) {
    debugLog(message);
  }
  try {
    return isMutation
      ? await apollo.registrado.mutate<T>({ mutation: query, variables })
      : await apollo.registrado.query<T>({
          query,
          variables,
          errorPolicy: 'all',
          fetchPolicy: 'network-only',
        });
  } catch (err) {
    throw new Error('Apollo operation failed: ' + err.message);
  }
}

export const useCurrentUserStore = defineStore('current-user', {
  state: (): CurrentUserState => {
    return {
      jwt: null,
      user: null,
      isLoggedIn: null,
      konfirmita: null,
      isAdmin: null,
      authtoken: null,
      csrftoken: null,
      organizations: null,
      authData: null,
      counter: 0,
      ws_connected: false,
    };
  },
  getters: {
    getUser: (state): any => state.user,

    getAvatar: (state): string | null => {
      return state.user?.avataro?.bildoF?.url
        ? state.user.avataro.bildoF.url
        : null;
    },

    getUserId: (state): number | null => {
      if (state.user) {
        return state.user.objId;
      }
      return null;
    },
    getJWTpayload: (state): string[] => {
      if (!state.jwt) {
        return [];
      }

      return state.jwt.split('.').map((part: string) => {
        try {
          return JSON.parse(atob(part));
        } catch {
          return part;
        }
      });
    },
    getJWTisExpired: (state): number => {
      const payload = state.getJWTpayload;
      if (payload.length > 1 && payload[1].hasOwnProperty('exp')) {
        const currentTime = new Date().getTime();
        const expTime = new Date(
          (payload[1] as { exp: number }).exp * 1000,
        ).getTime();
        return expTime - currentTime;
      }
      return -1;
    },
    getUserName: (state) => {
      return (
        state.user?.universoUzanto?.retnomo ||
        state.user?.unuaNomo?.enhavo ||
        state.user?.duaNomo?.enhavo ||
        state.user?.familinomo?.enhavo ||
        state.user?.chefaRetposhto ||
        null
      );
    },
    getUserEmail: (state) => {
      return state.user?.chefaRetposhto || null;
    },
    getMyOrganizations: (state) => {
      if (state.organizations && Array.isArray(state.organizations.edges)) {
        return state.organizations.edges.map((item) => item.node.uuid);
      } else {
        return [];
      }
    },
    getAuthData: (state) => {
      return {
        user: state.user,
        isLoggedIn: state.isLoggedIn,
        konfirmita: state.konfirmita,
        isAdmin: state.isAdmin,
      };
    },
  },
  actions: {
    setCounter() {
      this.counter++;
    },
    getLocalStorage(): Promise<void> {
      const jwt_token = getStorageItem('jwt_token');
      const csrf_token = getStorageItem('csrf_token');
      const auth_token = getStorageItem('auth_token');

      this.jwt = jwt_token;
      this.csrftoken = csrf_token;
      this.authtoken = auth_token;

      if (this.getJWTisExpired < 0) {
        this.isLoggedIn = false;
        this.clearLocalStorage();
        return Promise.reject(new Error('JWT Expired'));
      }

      this.isLoggedIn = !!jwt_token && !!csrf_token && !!auth_token;
      return this.isLoggedIn
        ? Promise.resolve()
        : Promise.reject(new Error('Missing tokens'));
    },
    setLocalStorage(save: boolean): void {
      setStorageItem('jwt_token', this.jwt ?? '', save);
      setStorageItem('csrf_token', this.csrftoken ?? '', save);
      setStorageItem('auth_token', this.authtoken ?? '', save);
    },
    clearLocalStorage(): void {
      clearStorage(['jwt_token', 'csrf_token', 'auth_token']);
      this.$reset();
    },
    async login(
      login: string,
      password: string,
      save: boolean = true,
    ): Promise<any> {
      try {
        const variables = { login, password };
        const result = await executeApolloOperation<{ ensaluti: any }>({
          isMutation: true,
          query: login_query,
          variables,
          message: 'Login >>',
        });

        if (result.data.ensaluti.status) {
          this.jwt = result.data.ensaluti.jwtToken;
          this.authtoken = result.data.ensaluti.token;
          this.csrftoken = result.data.ensaluti.csrfToken;
          this.setLocalStorage(save);

          // Устанавливаем статус входа в систему
          this.isLoggedIn = true;

          // Возвращаем результат операции для дальнейшей обработки
          return result.data.ensaluti;
        } else {
          // Сохраняем статус подтверждения, если он доступен
          this.konfirmita = result.data.ensaluti.konfirmita;

          // Выбрасываем ошибку с подробной информацией
          throw new Error('Login failed: ' + result.data.ensaluti.message);
        }
      } catch (err) {
        // Обработка ошибки, возникшей в процессе выполнения мутации
        throw new Error('Login error: ' + err.message);
      }
    },
    async logout(): Promise<void> {
      try {
        const result = await executeApolloOperation<{ elsaluti: any }>({
          isMutation: true,
          query: logout_query,
          message: 'Logout >>',
        });

        if (result.data.elsaluti.status) {
          // Очистка локального хранилища и состояния после успешного выхода
          this.clearLocalStorage();
          this.isLoggedIn = false;

          // Можно возвращать информацию о статусе выхода, если это необходимо
          return result.data.elsaluti;
        } else {
          // Обработка ситуации, когда мутация выхода не выполнена успешно
          throw new Error('Logout failed: ' + result.data.elsaluti.message);
        }
      } catch (err) {
        // Обработка ошибок запроса или сетевых ошибок
        throw new Error('Logout error: ' + err.message);
      }
    },
    async getMe(): Promise<Mi> {
      try {
        const result = await executeApolloOperation<MiWrapper>({
          isMutation: false,
          query: mi,
          message: 'get Me >>',
        });

        if (result.data) {
          this.user = result.data.mi;
          this.isAdmin = result.data.mi.isAdmin;
          this.isLoggedIn = true;
          this.konfirmita = result.data.mi.konfirmita;
          await this.getLocalStorage(); // Обновляем данные из локального хранилища

          // Опционально: Можно вернуть полученные данные пользователя
          return result.data.mi as unknown as Mi;
        } else {
          // Если данные пользователя не получены, очищаем состояние и локальное хранилище
          this.clearLocalStorage();
          throw new Error('Failed to retrieve user data');
        }
      } catch (err) {
        // Обработка ошибок запроса или сетевых ошибок
        throw new Error('Error fetching user data: ' + err.message);
      }
    },
    fetchOrganizations(payload) {
      return apollo.default
        .query({
          query: listOrganizaiton,
          variables: payload,
          errorPolicy: 'all',
          fetchPolicy: 'network-only',
        })
        .then((data) => {
          const organizations = data.data.organizo;
          this.organizations = organizations;
          // return Promise.resolve(organizations);
        })
        .catch((error) => Promise.reject(error));
    },
    addUniversoUzanto(variables) {
      return apollo.default
        .mutate({
          mutation: addUniversoUzanto,
          variables: variables,
        })
        .then((data) => {
          const resp = data.data.redaktuProfilo;
          this.getMe();
          return Promise.resolve(resp);
        })
        .catch((err) => {
          // Ошибка запроса на уровне сети/протокола
          return Promise.reject(err);
        });
    },
  },
});
