import axiosInstance, { type RawResp } from '@/api';
import router from '@/router';
import type { UserInfo } from '@/schemas';
import { type idAndNameSchema, userInfoResponseSchema } from '@/schemas';
import { errorMessage } from '@/utils/api';
import type { MaybePromise } from '@/utils/types';
import { StorageSerializers, useLocalStorage } from '@vueuse/core';
import { AxiosError } from 'axios';
import { ElMessage } from 'element-plus';
import { defineStore } from 'pinia';
import { computed, onUnmounted, ref, watch } from 'vue';
import { z } from 'zod';

type Permission = z.infer<typeof idAndNameSchema>;
export const useUserStore = defineStore('user', () => {
  const token = useLocalStorage<string | null>('token', null, {
    serializer: StorageSerializers.string,
  });
  const userInfo = useLocalStorage<UserInfo | null>('user-info', null, {
    serializer: StorageSerializers.object,
  });
  const permissions = computed<Permission[]>(() => userInfo.value?.auth.permissions ?? []);
  const initializing = ref(false);
  const logined = computed(() => (userInfo.value && userInfo.value.id !== -1) ?? false);
  watch(
    userInfo,
    (info) => {
      router.push({
        path: info && info.id !== -1 ? router.currentRoute.value.fullPath : '/',
        force: true,
      });
    },
    { flush: 'sync' },
  );
  async function updateSelfUserInfo(showErrorMessage: boolean): Promise<boolean> {
    initializing.value = true;
    try {
      const raw = await axiosInstance
        .get<RawResp>('/api/user/info')
        .then((r) => r.data)
        .catch((e: AxiosError) => {
          if (!showErrorMessage) return;
          ElMessage.error(errorMessage('获取用户信息失败', e.code, e.message));
        });
      const resp = userInfoResponseSchema.parse(raw);
      if (resp.type === 'error') {
        if (showErrorMessage) {
          ElMessage.error(errorMessage('获取用户信息失败', resp.code, resp.msg));
        }
        return false;
      }
      userInfo.value = resp.data;
      return true;
    } finally {
      initializing.value = false;
    }
  }
  function hasPermission(permissionId: number) {
    return permissions.value.find((permission) => permission.id === permissionId);
  }
  const logoutInterceptors = new Set<LogoutInterceptor>();
  function addLogoutInterceptor(fn: LogoutInterceptor) {
    logoutInterceptors.add(fn);
  }
  function removeLogoutInterceptor(fn: LogoutInterceptor) {
    logoutInterceptors.delete(fn);
  }
  async function logout() {
    const res = await Promise.all(Array.from(logoutInterceptors).map((fn) => fn()));
    if (!res.every((r) => r === undefined || r)) return false;
    token.value = null;
    await updateSelfUserInfo(true);
    return true;
  }
  return {
    token,
    userInfo,
    logined,
    permissions,
    initializing,
    updateSelfUserInfo,
    hasPermission,
    logoutInterceptors,
    addLogoutInterceptor,
    removeLogoutInterceptor,
    logout,
  };
});
export type LogoutInterceptor = () => MaybePromise<boolean | void>;
export function useLogoutInterceptor(fn: LogoutInterceptor) {
  const userStore = useUserStore();
  userStore.addLogoutInterceptor(fn);
  const remove = () => {
    userStore.removeLogoutInterceptor(fn);
  };
  onUnmounted(remove);
  return remove;
}
