import { defineStore } from 'pinia'
import type { AuthUser, EffectivePermissions, LoginRequest, RunMode, Scope } from '@/types/auth'
import {
  login as apiLogin,
  logout as apiLogout,
  switchScope as apiSwitchScope,
  getMe as apiGetMe
} from '@/api/auth'

interface AuthState {
  token: string | null
  user: AuthUser | null
  scope: Scope | null
  runMode: RunMode | null
  effectivePermissions: EffectivePermissions | null
  /**
   * 本次浏览器会话内是否向后端实校过 token。
   * 仅内存态，不写 localStorage —— 页面刷新后必须重新 validate，确保 Platform.Server
   * 在用户离开期间重启（JWT secret 重生）的场景下能立刻被发现并跳登录。
   */
  validated: boolean
}

const TOKEN_KEY = 'simple.auth.token'
const USER_KEY = 'simple.auth.user'
const SCOPE_KEY = 'simple.auth.scope'
const RUN_MODE_KEY = 'simple.auth.runMode'
const PERM_KEY = 'simple.auth.perm'

// 会话 45 HC-2：每个字段独立 try/catch，避免一个 corrupt 字段把整个 state 干净化。
function safeJsonParse<T>(key: string): T | null {
  try {
    const raw = localStorage.getItem(key)
    return raw == null ? null : (JSON.parse(raw) as T)
  } catch {
    return null
  }
}

function safeReadString(key: string): string | null {
  try { return localStorage.getItem(key) } catch { return null }
}

function loadState(): AuthState {
  return {
    token: safeReadString(TOKEN_KEY),
    user: safeJsonParse<AuthUser>(USER_KEY),
    scope: safeReadString(SCOPE_KEY) as Scope | null,
    runMode: safeReadString(RUN_MODE_KEY) as RunMode | null,
    effectivePermissions: safeJsonParse<EffectivePermissions>(PERM_KEY),
    // 刷新页面后默认未校验：路由守卫会在受保护路由首次进入前 await validate()。
    validated: false
  }
}

/** 清空登录态（内存 + localStorage）。validate 失败 / logout / 401 拦截共用，避免 5 行重复。 */
function clearLocalAuth(target: AuthState) {
  target.token = null
  target.user = null
  target.scope = null
  target.runMode = null
  target.effectivePermissions = null
  target.validated = false
  try {
    localStorage.removeItem(TOKEN_KEY)
    localStorage.removeItem(USER_KEY)
    localStorage.removeItem(SCOPE_KEY)
    localStorage.removeItem(RUN_MODE_KEY)
    localStorage.removeItem(PERM_KEY)
  } catch { /* 隐私模式 / iframe 沙箱可能禁用 localStorage */ }
}

export const useAuthStore = defineStore('auth', {
  state: (): AuthState => loadState(),
  getters: {
    isAuthed: (s) => !!s.token,
    hasOp:
      (s) =>
      (code: string): boolean => {
        const ops = s.effectivePermissions?.allowedOps ?? []
        return ops.includes('*') || ops.includes(code)
      },
    widgetOf:
      (s) =>
      (widgetId: string): 'hidden' | 'readonly' | 'interactive' => {
        const grant = s.effectivePermissions?.visibleWidgets.find((w) => w.widgetId === widgetId)
        return grant?.visibility ?? 'interactive'
      },
    /**
     * 当前用户在当前 scope 下是否可访问指定页面（route.name）。
     * '*' 视为全部页面（兜底兼容）。菜单过滤与路由守卫共用此判断。
     */
    hasPage:
      (s) =>
      (key: string): boolean => {
        const pages = s.effectivePermissions?.allowedPages ?? []
        return pages.includes('*') || pages.includes(key)
      }
  },
  actions: {
    async login(req: LoginRequest) {
      const resp = await apiLogin(req)
      this.token = resp.token
      this.user = resp.user
      this.scope = resp.scope
      this.runMode = resp.runMode
      this.effectivePermissions = resp.effectivePermissions
      // 登录响应本身就是后端的身份背书，等同于一次成功的 /me；省一次往返。
      this.validated = true
      localStorage.setItem(TOKEN_KEY, resp.token)
      localStorage.setItem(USER_KEY, JSON.stringify(resp.user))
      localStorage.setItem(SCOPE_KEY, resp.scope)
      localStorage.setItem(RUN_MODE_KEY, resp.runMode)
      localStorage.setItem(PERM_KEY, JSON.stringify(resp.effectivePermissions))
      return resp
    },
    async logout() {
      try { await apiLogout() } catch { /* ignore */ }
      clearLocalAuth(this)
    },
    /**
     * 用本地 token 让后端实校一次身份。
     * - 成功：刷新 user/scope/runMode/perm 并标记 validated=true。
     * - 失败（401/403/网络/etc）：清空登录态并抛出错误，由调用方（路由守卫）跳 /login。
     * - mock 模式下 apiGetMe 主动抛 'mock-mode-skip-me-validation'，视为「无须实校」直接放行。
     */
    async validate() {
      if (!this.token) {
        // 没 token 直接清干净，避免有半残数据导致 isAuthed 抖动。
        clearLocalAuth(this)
        throw new Error('no-token')
      }
      try {
        const me = await apiGetMe()
        this.user = me.user
        this.scope = me.scope
        this.runMode = me.runMode
        this.effectivePermissions = me.effectivePermissions
        this.validated = true
        localStorage.setItem(USER_KEY, JSON.stringify(me.user))
        localStorage.setItem(SCOPE_KEY, me.scope)
        localStorage.setItem(RUN_MODE_KEY, me.runMode)
        localStorage.setItem(PERM_KEY, JSON.stringify(me.effectivePermissions))
      } catch (err) {
        if (err instanceof Error && err.message === 'mock-mode-skip-me-validation') {
          this.validated = true
          return
        }
        clearLocalAuth(this)
        throw err
      }
    },
    // AR-6 (会话 45)：原实现客户端直接改 `allowedOps = ['*']` —— 是伪权限，
    // 既不安全（后端不再校验时立刻翻车）也容易和后端 ops list 漂移。
    // 改为请求 /api/auth/switch-scope 让服务端按账号实际允许的 scope 重发 token + perms。
    // 后端若不允许该 scope（如 ops 账号尝试切到 Platform），会返回 403 → 由调用方 toast 提示。
    async switchScope(target: Scope) {
      const resp = await apiSwitchScope(target)
      this.token = resp.token
      this.user = resp.user
      this.scope = resp.scope
      this.runMode = resp.runMode
      this.effectivePermissions = resp.effectivePermissions
      // SwitchScope 后端重发了 token + perm，等同于一次成功的 /me，保持 validated 为 true。
      this.validated = true
      localStorage.setItem(TOKEN_KEY, resp.token)
      localStorage.setItem(USER_KEY, JSON.stringify(resp.user))
      localStorage.setItem(SCOPE_KEY, resp.scope)
      localStorage.setItem(RUN_MODE_KEY, resp.runMode)
      localStorage.setItem(PERM_KEY, JSON.stringify(resp.effectivePermissions))
      return resp
    }
  }
})
