import { useNavigate } from 'react-router'
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
import { navigate } from '@/router/components/SetupNavigation'
import type { UserToken } from '#/entity'
import { StorageEnum } from '#/enum'
import { toast } from 'sonner'
import { loginByUsername } from '@/api/services/login'
import { getMenu } from '@/api/services/menu'
import { getUserDetail, getUserInfo } from '@/api/services/user'
import { deepClone, encryption } from '@/utils/util'
import { isURL } from '@/utils/validate'
import type { ILoginForm } from '@/pages/sys/login/LoginForm'

const { VITE_APP_HOMEPAGE: HOMEPAGE } = import.meta.env

function addPath(ele: API.MenuResultItem & Record<string, any>, first?: boolean) {
  const menu = {
    props: {
      label: 'name',
      path: 'path',
      icon: 'icon',
      children: 'children'
    }
  }
  const propsConfig = menu.props
  const propsDefault = {
    label: propsConfig.label || 'name',
    path: propsConfig.path || 'path',
    icon: propsConfig.icon || 'icon',
    children: propsConfig.children || 'children'
  }
  const isChild = ele[propsDefault.children] && ele[propsDefault.children].length !== 0
  if (!isChild) ele[propsDefault.children] = []
  if (!isChild && first && !isURL(ele[propsDefault.path])) {
    ele[propsDefault.path] = ele[propsDefault.path] + '/index'
  } else {
    ele[propsDefault.children].forEach((child: API.MenuChildrenItem) => {
      addPath(child)
    })
  }
}

type UserStore = {
  userInfo: Partial<API.SysUser>
  tenantInfo: Partial<API.UserDetail>
  permissions: Record<string, boolean>
  menu: API.MenuResult
  userToken: UserToken
  expires_in: number | null
  tenant_id?: number
  access_token: string
  refresh_token: string
  // 使用 actions 命名空间来存放所有的 action
  actions: {
    setUserInfo: (userInfo: API.SysUser) => void
    setTenantInfo: (userInfo: API.UserDetail) => void
    setTenantId: (tenant_id: number) => void
    setExpiresIn: (expires_in: number) => void
    setAccessToken: (access_token: string) => void
    setRefreshToken: (refresh_token: string) => void
    setMenu: (params: { menu?: UserStore['menu']; type?: boolean }) => void
    setPermissions: (permissions: string[]) => void
    logout: () => void
    getUserDetail: () => Promise<void>
    getUserInfo: () => Promise<void>
  }
}

export const useUserSessionStore = create<UserStore>()(
  persist(
    (set, get) => ({
      userInfo: {},
      tenantInfo: {},
      permissions: {},
      menu: [],
      expires_in: null,
      tenant_id: undefined,
      access_token: '',
      refresh_token: '',
      userToken: {},
      actions: {
        setUserInfo: (userInfo) => {
          set({ userInfo })
        },
        setTenantInfo(tenantInfo) {
          set({ tenantInfo })
        },
        setTenantId: (tenant_id) => {
          set({ tenant_id })
        },
        setExpiresIn: (expires_in) => {
          set({ expires_in })
        },
        setAccessToken: (access_token) => {
          set({ access_token })
        },
        setRefreshToken: (refresh_token) => {
          set({ refresh_token })
        },
        setMenu: (params = {}) => {
          const { menu, type } = params
          if (type !== false) set({ menu: menu as UserStore['menu'] })
        },
        setPermissions: (permissions) => {
          const list: Record<string, boolean> = {}
          for (let i = 0; i < permissions.length; i++) {
            list[permissions[i]] = true
          }
          set({ permissions: list })
        },
        logout() {
          set({
            userInfo: {},
            tenantInfo: {},
            permissions: {},
            menu: [],
            expires_in: null,
            tenant_id: undefined,
            access_token: '',
            refresh_token: ''
          })
          navigate('/login')
        },
        getUserInfo() {
          const { actions } = get()
          return new Promise<void>((resolve) => {
            getUserInfo().then(async (res) => {
              if (res) {
                const data = res.data
                actions.setUserInfo(data.sysUser)
                actions.setPermissions(data.permissions || [])
                actions.getUserDetail()
                resolve()
              }
            })
          })
        },
        getUserDetail() {
          const currentState = get()
          return new Promise<void>((resolve) => {
            getUserDetail(currentState.userInfo.userId!).then((res) => {
              if (res) {
                currentState.actions.setTenantInfo(res.data)
                resolve()
              }
            })
          })
        }
      }
    }),
    {
      name: 'userSessionStore', // name of the item in the storage (must be unique)
      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
      partialize: (state) => ({
        [StorageEnum.TenantId]: state.tenant_id,
        [StorageEnum.AccessToken]: state.access_token,
        [StorageEnum.ExpiresIn]: state.expires_in,
        [StorageEnum.RefreshToken]: state.refresh_token,
        [StorageEnum.UserInfo]: state.userInfo,
        [StorageEnum.TenantInfo]: state.tenantInfo,
        [StorageEnum.Menu]: state.menu,
        [StorageEnum.Permissions]: state.permissions
      })
    }
  )
)
// 如果需要持久化到localStorage, 则使用下面的代码
// type UserLocalStore = {
//   actions: {}
// }
// const useUserLocalStore = create<UserLocalStore>()(
//   persist(
//     (_set) => ({
//       actions: {}
//     }),
//     {
//       name: 'userLocalStore', // name of the item in the storage (must be unique)
//       storage: createJSONStorage(() => localStorage), // (optional) by default, 'localStorage' is used
//       partialize: (_state) => ({})
//     }
//   )
// )

// 如果需要在一个store中分别持久化到localStorage和sessionStorage, 则使用下面的代码合并sessionStore和localStore. 否则直接导出一个store即可.
const useUserStore = () => {
  const userSessionStore = useUserSessionStore()
  // const userLocalStore = useUserLocalStore();

  return {
    ...userSessionStore,
    // ...userLocalStore,
    actions: {
      ...userSessionStore.actions
      // ...userLocalStore.actions,
    }
  }
}

export const useUserInfo = () => useUserSessionStore((state) => state.userInfo)
export const useTenantId = () => useUserSessionStore((state) => state.tenant_id)
export const useExpiresIn = () => useUserSessionStore((state) => state.expires_in)
export const useAccessToken = () => useUserSessionStore((state) => state.access_token)
export const useRefreshToken = () => useUserSessionStore((state) => state.refresh_token)
export const useUserToken = () => useUserSessionStore((state) => state.userToken)
export const useUserMenu = () => useUserSessionStore((state) => state.menu)
export const useUserPermission = () => useUserSessionStore((state) => state.permissions)
export const useUserActions = () => useUserSessionStore((state) => state.actions)

// 根据用户名登录
export const useLoginByUsername = () => {
  const getMenuFn = useGetMenu()
  const { setAccessToken, setRefreshToken, setExpiresIn } = useUserActions()
  const loginFn = (userInfo: Omit<ILoginForm, 'tenant_name'>) => {
    const user = encryption({
      data: userInfo,
      key: 'REmAZJQfbiDpdXCf',
      param: ['password']
    })
    return new Promise<void>((resolve, reject) => {
      loginByUsername(user.username, user.password, user.code, user.randomStr)
        .then(async (res) => {
          if (res) {
            const data = res
            setAccessToken(data.access_token)
            setRefreshToken(data.refresh_token)
            setExpiresIn(data.expires_in)
            await getMenuFn()
            toast.success('登录成功！', { position: 'top-center' })
            resolve()
          } else {
            reject('获取用户信息失败')
          }
        })
        .catch((err) => {
          toast.error(err || '登录失败！', { position: 'top-center' })
          reject(err)
        })
    })
  }
  return loginFn
}

export const useGetMenu = () => {
  const navigate = useNavigate()
  const { setMenu } = useUserActions()
  return (obj?: { id?: number; type?: boolean }) => {
    return new Promise((resolve) => {
      getMenu(obj?.id as number).then((res) => {
        if (res) {
          const menu: API.MenuResult = deepClone(res.data)
          menu.forEach((ele) => {
            addPath(ele)
          })
          const type = obj?.type
          setMenu({
            type,
            menu
          })
          navigate(HOMEPAGE, { replace: true })
          resolve(menu)
        }
      })
    })
  }
}

export default useUserStore
