// =============================================================================
// 企微IT智能服务台 — H5用户端员工状态管理（Pinia Store）
// =============================================================================
// 说明：管理员工认证状态，包括：
//   1. Bearer Token 管理（保存、读取、清除）
//   2. 用户信息管理（姓名、部门、岗位等）
//   3. OAuth2 授权流程（回调处理、重新授权）
//   4. Mock 登录（测试阶段，跳过 OAuth2）
// =============================================================================

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import {
  oauthCallback,
  mockLogin as mockLoginApi,
  getEmployeeInfo,
  getOAuthAuthorizeUrl,
  type OAuthCallbackResponse,
  type EmployeeInfo,
} from '@/api/employee'
// Bug #2 修复：从独立模块导入回调注册函数，避免循环依赖
import { registerAuthExpiredHandler } from '@/utils/authCallback'

// --------------------------------------------------------------------------
// localStorage Key 常量
// --------------------------------------------------------------------------
const TOKEN_KEY = 'h5_token'
const PORTAL_TOKEN_KEY = 'portal_token'
const EMPLOYEE_ID_KEY = 'employee_id'
const EMPLOYEE_NAME_KEY = 'employee_name'
/** OAuth2 重定向计数器 key（防止无限重定向循环） */
const OAUTH_REDIRECT_COUNT_KEY = 'oauth_redirect_count'
/** 最大允许重定向次数（超过此值停止重定向，避免无限循环） */
const OAUTH_MAX_REDIRECT_COUNT = 3

// --------------------------------------------------------------------------
// Store 定义
// --------------------------------------------------------------------------
export const useEmployeeStore = defineStore('employee', () => {

  // ==========================================================================
  // 响应式状态
  // ==========================================================================

  /** 访问令牌（Bearer Token）— 优先从 h5_token 读取，降级读取 portal_token */
  const token = ref<string>(localStorage.getItem(TOKEN_KEY) || localStorage.getItem(PORTAL_TOKEN_KEY) || '')

  /** 当前员工信息 */
  const employeeInfo = ref<EmployeeInfo | null>(null)

  /** 是否正在认证中（OAuth2回调处理中） */
  const authenticating = ref<boolean>(false)

  // ==========================================================================
  // 计算属性
  // ==========================================================================

  /** 是否已认证（必须有有效的 Bearer Token） */
  const isAuthenticated = computed(() => {
    // 只检查 token，不检查 employee_id
    // employee_id 单独存在 localStorage 不代表已认证（可能是残留数据）
    if (!token.value) return false

    // Bug #1 修复：检查 JWT token 是否过期
    // 如果 token 是 JWT 格式（含 exp 字段），验证是否已过期
    // 如果不是 JWT 格式（无法解析），放行让后端校验
    return !isTokenExpired(token.value)
  })

  /** 当前员工ID */
  const employeeId = computed(() => employeeInfo.value?.employee_id || localStorage.getItem(EMPLOYEE_ID_KEY) || '')

  /** 当前员工姓名 */
  const employeeName = computed(() => employeeInfo.value?.employee_name || localStorage.getItem(EMPLOYEE_NAME_KEY) || '')

  // ==========================================================================
  // 内部方法
  // ==========================================================================

  /**
   * 检查 JWT token 是否已过期
   * 解析 JWT payload 中的 exp 字段，与当前时间比较。
   * 包含 60 秒安全余量，防止客户端与服务端时钟偏差导致的问题。
   *
   * @param jwtToken JWT 格式的 token 字符串
   * @returns true 表示已过期或格式异常，false 表示仍有效
   */
  function isTokenExpired(jwtToken: string): boolean {
    try {
      const parts = jwtToken.split('.')
      if (parts.length !== 3) {
        // 非标准 JWT 格式（可能是自定义 token），无法判断过期，放行让后端校验
        return false
      }
      const payload = JSON.parse(atob(parts[1]))
      if (!payload.exp) {
        // JWT 中无 exp 字段（永不过期或非标准），放行
        return false
      }
      // exp 是秒级 Unix 时间戳；提前 60 秒视为过期（安全余量）
      const nowSeconds = Math.floor(Date.now() / 1000)
      return nowSeconds >= payload.exp - 60
    } catch {
      // 解析失败（base64 解码或 JSON 解析出错），不阻塞用户，放行让后端校验
      return false
    }
  }

  /**
   * 保存 token 到状态和 localStorage
   * @param newToken 新的访问令牌
   */
  function _saveToken(newToken: string): void {
    token.value = newToken
    localStorage.setItem(TOKEN_KEY, newToken)
  }

  /**
   * 清除认证状态
   * 移除 token 和员工信息
   */
  function _clearAuth(): void {
    token.value = ''
    employeeInfo.value = null
    localStorage.removeItem(TOKEN_KEY)
    localStorage.removeItem(EMPLOYEE_ID_KEY)
    localStorage.removeItem(EMPLOYEE_NAME_KEY)
  }

  /**
   * 重置 OAuth2 重定向计数器
   * 在登录成功后调用，清除之前的重定向计数
   */
  function _resetRedirectCount(): void {
    localStorage.removeItem(OAUTH_REDIRECT_COUNT_KEY)
  }

  /**
   * 递增 OAuth2 重定向计数器，并返回当前计数
   * @returns 当前重定向次数
   */
  function _incrementRedirectCount(): number {
    const current = parseInt(localStorage.getItem(OAUTH_REDIRECT_COUNT_KEY) || '0', 10)
    const next = current + 1
    localStorage.setItem(OAUTH_REDIRECT_COUNT_KEY, String(next))
    return next
  }

  /**
   * 检查是否已超过 OAuth2 最大重定向次数
   * @returns true 表示已超过阈值，应停止重定向
   */
  function _isRedirectLoop(): boolean {
    const current = parseInt(localStorage.getItem(OAUTH_REDIRECT_COUNT_KEY) || '0', 10)
    return current >= OAUTH_MAX_REDIRECT_COUNT
  }

  // ==========================================================================
  // 公共方法
  // ==========================================================================

  /**
   * 处理 OAuth2 授权回调
   * 将企微 OAuth2 授权码传给后端，换取 token 和员工身份
   * 成功后保存 token 和基本信息到 localStorage
   *
   * @param code 企微 OAuth2 授权码
   * @param state 企微 OAuth2 state 参数（可选）
   * @returns OAuth2 回调返回的数据
   * @throws 授权失败时抛出异常
   */
  async function handleOAuthCallback(code: string, state?: string): Promise<OAuthCallbackResponse> {
    authenticating.value = true
    try {
      const data = await oauthCallback({ code, state })

      // 保存 token
      _saveToken(data.token)

      // 保存基本信息到 localStorage（供降级和快速读取使用）
      localStorage.setItem(EMPLOYEE_ID_KEY, data.employee_id)
      localStorage.setItem(EMPLOYEE_NAME_KEY, data.employee_name)

      // 填充员工信息
      employeeInfo.value = {
        employee_id: data.employee_id,
        employee_name: data.employee_name,
        department: data.department,
        position: data.position,
        mobile: '',
        email: '',
        avatar: data.avatar,
        is_vip: false,
      }

      console.log('[EmployeeStore] OAuth2 登录成功:', data.employee_name)
      // 登录成功后重置重定向计数器，清除之前的循环记录
      _resetRedirectCount()
      return data
    } catch (error) {
      console.error('[EmployeeStore] OAuth2 登录失败:', error)
      throw error
    } finally {
      authenticating.value = false
    }
  }

  /**
   * Mock 登录（测试阶段，跳过 OAuth2）
   * 调用后端 /api/h5/mock-login 获取真实 Bearer Token
   * 仅当后端 MOCK_LOGIN_ENABLED=true 时可用
   *
   * @param empId 员工 ID
   * @param empName 员工姓名（可选）
   * @returns 登录返回的数据
   * @throws 登录失败时抛出异常
   */
  async function mockLogin(empId: string, empName?: string): Promise<OAuthCallbackResponse> {
    authenticating.value = true
    try {
      const data = await mockLoginApi({
        employee_id: empId,
        employee_name: empName || '测试用户',
      })

      // 保存 token
      _saveToken(data.token)

      // 保存基本信息到 localStorage
      localStorage.setItem(EMPLOYEE_ID_KEY, data.employee_id)
      localStorage.setItem(EMPLOYEE_NAME_KEY, data.employee_name)

      // 填充员工信息
      employeeInfo.value = {
        employee_id: data.employee_id,
        employee_name: data.employee_name,
        department: data.department,
        position: data.position,
        mobile: '',
        email: '',
        avatar: data.avatar,
        is_vip: false,
      }

      console.log('[EmployeeStore] Mock 登录成功:', data.employee_name)
      _resetRedirectCount()
      return data
    } catch (error) {
      console.error('[EmployeeStore] Mock 登录失败:', error)
      throw error
    } finally {
      authenticating.value = false
    }
  }

  /**
   * 获取当前员工详细信息
   * 从后端 /api/h5/me 获取最新的员工信息
   */
  async function fetchEmployeeInfo(): Promise<void> {
    if (!token.value) return

    try {
      const data = await getEmployeeInfo()
      employeeInfo.value = data
      // 同步更新 localStorage
      localStorage.setItem(EMPLOYEE_ID_KEY, data.employee_id)
      localStorage.setItem(EMPLOYEE_NAME_KEY, data.employee_name)
      console.log('[EmployeeStore] 获取员工信息成功:', data.employee_name)
    } catch (error) {
      console.error('[EmployeeStore] 获取员工信息失败:', error)
      // 开发模式：API 失败时使用 mock 数据，不阻塞初始化
      if (!import.meta.env.VITE_WECOM_CORP_ID) {
        console.warn('[EmployeeStore] 开发模式：使用 mock 员工信息')
        employeeInfo.value = {
          employee_id: localStorage.getItem(EMPLOYEE_ID_KEY) || 'dev_test_employee',
          employee_name: '开发测试用户',
          department: 'IT部',
          position: '开发工程师',
          mobile: '',
          email: '',
          avatar: '',
          is_vip: false,
        }
        return
      }
      throw error
    }
  }

  /**
   * 跳转企微 OAuth2 授权页面
   * 优先从后端获取授权URL（后端知道正确的 CORP_ID），传入当前页面地址作为回调
   * 失败则本地构造
   */
  async function redirectToOAuth(): Promise<void> {
    // 当前页面的完整回调地址
    const currentRedirectUri = window.location.origin + '/itdesk/'

    try {
      // 优先从后端获取授权URL（后端知道正确的 CORP_ID）
      const data = await getOAuthAuthorizeUrl()
      if (data.authorize_url) {
        window.location.href = data.authorize_url
        return
      }
    } catch (error) {
      console.warn('[EmployeeStore] 从后端获取授权URL失败，尝试本地构造:', error)
    }

    // 降级：本地构造授权URL
    const corpId = import.meta.env.VITE_WECOM_CORP_ID || ''
    if (!corpId) {
      console.warn('[EmployeeStore] 未配置 VITE_WECOM_CORP_ID，无法跳转授权')
      return
    }

    const redirectUri = encodeURIComponent(currentRedirectUri)
    const oauthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${corpId}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`
    window.location.href = oauthUrl
  }

  /**
   * 处理 401 未授权错误
   * 清除认证状态，重新跳转 OAuth2 授权
   * 加入防循环机制：当短时间内多次重定向时，停止跳转并提示用户
   */
  async function handleUnauthorized(): Promise<void> {
    console.warn('[EmployeeStore] Token 已过期或无效，重新授权')

    // 防循环检测：超过最大重定向次数时停止跳转
    if (_isRedirectLoop()) {
      console.error('[EmployeeStore] OAuth2 重定向次数超限，疑似无限循环，停止重定向')
      _clearAuth()
      _resetRedirectCount()
      // 显示错误提示，引导用户手动操作
      const { showToast } = await import('vant')
      showToast('登录状态异常，请刷新页面重试')
      return
    }

    _clearAuth()
    // 递增重定向计数
    const count = _incrementRedirectCount()
    console.warn(`[EmployeeStore] OAuth2 重定向计数: ${count}/${OAUTH_MAX_REDIRECT_COUNT}`)
    await redirectToOAuth()
  }

  /**
   * 本地开发降级登录
   * 在非企微环境下手动输入 employee_id 进行登录
   * 注意：此方式不设置 Bearer token（后端无法验证），
   * 而是保留 employee_id 在 localStorage，让 Axios 拦截器
   * 使用 X-Employee-Id 降级头发送身份信息
   *
   * @param empId 员工 ID
   */
  function devLogin(empId: string): void {
    localStorage.setItem(EMPLOYEE_ID_KEY, empId)
    // 注意：不设置 h5_token，让 API 拦截器使用 X-Employee-Id 降级头
    token.value = ''
    employeeInfo.value = {
      employee_id: empId,
      employee_name: '开发测试用户',
      department: 'IT部',
      position: '开发工程师',
      mobile: '',
      email: '',
      avatar: '',
      is_vip: false,
    }
    console.log('[EmployeeStore] 开发模式登录:', empId)
  }

  /**
   * 登出
   * 清除认证状态
   */
  function logout(): void {
    _clearAuth()
    console.log('[EmployeeStore] 已登出')
  }

  // ==========================================================================
  // Store 初始化：注册认证过期回调
  // ==========================================================================
  // Bug #2 修复：让 api/index.ts 通过 triggerAuthExpired() 触发 handleUnauthorized，
  // 避免 api/index.ts → stores/employee.ts 的循环 dynamic import。
  registerAuthExpiredHandler(handleUnauthorized)

  // ==========================================================================
  // 返回所有状态和方法
  // ==========================================================================
  return {
    // 状态
    token,
    employeeInfo,
    authenticating,

    // 计算属性
    isAuthenticated,
    employeeId,
    employeeName,

    // 方法
    handleOAuthCallback,
    mockLogin,
    fetchEmployeeInfo,
    redirectToOAuth,
    handleUnauthorized,
    devLogin,
    logout,
  }
})
