// =============================================================================
// 企微IT智能服务台 — H5用户端 WebSocket 组合式函数
// =============================================================================
// 说明：封装 H5 员工端的 WebSocket 连接管理，提供：
//   1. 自动连接 + 断线重连（指数退避，最大 30 秒）
//   2. 心跳保活（每 30 秒发送 ping）
//   3. 事件分发：收到消息后根据 type 调用对应 store 方法
//   4. 降级策略：WS 断连时自动启动轮询 fallback，WS 重连后自动停止轮询
//
// 与坐席端 useWebSocket 的区别：
//   - 连接端点不同：/ws/h5/{employee_id}（坐席端用 /ws/{agent_id}）
//   - 认证方式不同：使用 employee token（坐席端用 agent token）
//   - 事件处理不同：重点关注参与者变更事件（坐席端关注所有事件）
//   - 无 typing 发送能力（H5员工不需要发送 typing 指示器）
//
// 使用方式：
//   const { connect, disconnect } = useH5WebSocket()
//   onMounted(() => connect())
//   onUnmounted(() => disconnect())
// =============================================================================

import { useConversationStore } from '@/stores/conversation'
import { useEmployeeStore } from '@/stores/employee'

// --------------------------------------------------------------------------
// 常量配置
// --------------------------------------------------------------------------
/** 心跳间隔（毫秒）：每 30 秒发送一次 ping，保持连接存活 */
const HEARTBEAT_INTERVAL = 30000

/** 最大重连延迟（毫秒）：指数退避上限 30 秒 */
const MAX_RECONNECT_DELAY = 30000

/** 重连延迟基数（毫秒）：首次重连等待 1 秒 */
const RECONNECT_BASE_DELAY = 1000

/**
 * H5员工端 WebSocket 组合式函数
 *
 * 核心职责：
 * - 管理 WebSocket 连接的生命周期（建立、维持、断开、重连）
 * - 处理服务端推送的实时事件，分发到对应的 store
 * - 实现 WS → 轮询的自动降级和恢复
 *
 * 为什么用组合式函数（composable）：
 * - 遵循 Vue3 的组合式 API 模式，与组件生命周期绑定
 * - 与坐席端 useWebSocket 保持一致的架构风格
 */
export function useH5WebSocket() {
  // ==========================================================================
  // 内部状态
  // ==========================================================================

  /** WebSocket 实例 */
  let ws: WebSocket | null = null

  /** 心跳定时器 ID */
  let heartbeatTimer: ReturnType<typeof setInterval> | null = null

  /** 重连定时器 ID */
  let reconnectTimer: ReturnType<typeof setTimeout> | null = null

  /** 重连尝试次数（用于指数退避计算） */
  let reconnectAttempts = 0

  /** 是否主动断开（用户登出时设为 true，避免自动重连） */
  let intentionalDisconnect = false

  // ==========================================================================
  // 连接管理
  // ==========================================================================

  /**
   * 建立 H5 员工 WebSocket 连接
   *
   * 做什么：根据当前员工ID和token构建 WS URL，建立连接，注册事件处理函数
   * 为什么：H5员工需要实时接收参与者变更事件（新参与者加入、有人退出等）
   *
   * 连接 URL 格式：
   * - 开发环境：ws://localhost:8000/ws/h5/{employeeId}?token=xxx
   * - 生产环境：wss://domain.com/ws/h5/{employeeId}?token=xxx
   */
  function connect(): void {
    const employeeStore = useEmployeeStore()
    const employeeId = employeeStore.employeeId
    const token = employeeStore.token

    // 如果没有员工ID或token，说明未登录，不建立连接
    if (!employeeId || !token) {
      console.warn('[H5 WS] 未登录或缺少token，跳过连接')
      return
    }

    // 如果已有连接，先断开
    if (ws) {
      disconnect()
    }

    // 重置主动断开标记
    intentionalDisconnect = false

    // 构建 WebSocket URL
    // 开发环境：直接连后端 8000 端口（与坐席端一致）
    // 生产环境：通过同源 wss:// 连接（nginx 统一代理）
    const isDev = import.meta.env.DEV
    const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
    const wsHost = isDev ? 'localhost:8000' : window.location.host
    const wsUrl = `${wsProtocol}//${wsHost}/ws/h5/${employeeId}?token=${token}`

    console.log(`[H5 WS] 正在连接: ${wsUrl.replace(/token=[^&]+/, 'token=***')}`)
    ws = new WebSocket(wsUrl)

    // ----------------------------------------------------------------------
    // 连接成功
    // ----------------------------------------------------------------------
    ws.onopen = () => {
      console.log('[H5 WS] 连接成功')
      // 重置重连计数
      reconnectAttempts = 0
      // 启动心跳
      startHeartbeat()
      // WS 已连接，停止轮询 fallback
      // 为什么：WS 连接正常时不需要轮询，减少不必要的 HTTP 请求
      const store = useConversationStore()
      store.stopPolling()
    }

    // ----------------------------------------------------------------------
    // 收到消息
    // ----------------------------------------------------------------------
    ws.onmessage = (event: MessageEvent) => {
      try {
        const msg = JSON.parse(event.data)
        handleMessage(msg)
      } catch (error) {
        console.error('[H5 WS] 消息解析失败:', error)
      }
    }

    // ----------------------------------------------------------------------
    // 连接关闭
    // ----------------------------------------------------------------------
    ws.onclose = () => {
      console.log('[H5 WS] 连接关闭')
      // 停止心跳
      stopHeartbeat()
      // 清空 ws 引用
      ws = null

      // 如果不是主动断开，启动降级和重连
      if (!intentionalDisconnect) {
        // WS 断连，启动轮询 fallback
        // 为什么：WS 不可用时，仍需通过轮询获取最新数据
        const store = useConversationStore()
        store.startPolling()
        // 尝试重连
        scheduleReconnect()
      }
    }

    // ----------------------------------------------------------------------
    // 连接错误
    // ----------------------------------------------------------------------
    ws.onerror = (error: Event) => {
      console.error('[H5 WS] 连接错误:', error)
      // onclose 会自动触发，这里不需要额外处理
    }
  }

  /**
   * 主动断开 WebSocket 连接
   *
   * 做什么：关闭 WS 连接，清理定时器，标记为主动断开
   * 为什么：员工登出时需要主动断开，避免后台重连
   */
  function disconnect(): void {
    // 标记为主动断开，阻止自动重连
    intentionalDisconnect = true

    // 清理重连定时器
    if (reconnectTimer) {
      clearTimeout(reconnectTimer)
      reconnectTimer = null
    }

    // 清理心跳定时器
    stopHeartbeat()

    // 关闭 WebSocket 连接
    if (ws) {
      ws.close()
      ws = null
    }

    // 重置重连计数
    reconnectAttempts = 0

    console.log('[H5 WS] 已主动断开连接')
  }

  // ==========================================================================
  // 心跳保活
  // ==========================================================================

  /**
   * 启动心跳定时器
   *
   * 做什么：每 HEARTBEAT_INTERVAL 毫秒发送一次 ping 消息
   * 为什么：防止中间代理（Nginx、CDN 等）因空闲超时断开 WebSocket 连接
   */
  function startHeartbeat(): void {
    // 先清理旧定时器（避免重复）
    stopHeartbeat()

    heartbeatTimer = setInterval(() => {
      if (ws && ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'ping' }))
      }
    }, HEARTBEAT_INTERVAL)
  }

  /**
   * 停止心跳定时器
   */
  function stopHeartbeat(): void {
    if (heartbeatTimer) {
      clearInterval(heartbeatTimer)
      heartbeatTimer = null
    }
  }

  // ==========================================================================
  // 断线重连（指数退避）
  // ==========================================================================

  /**
   * 安排重连
   *
   * 做什么：根据指数退避算法计算延迟，安排下一次重连
   * 为什么：避免 WS 断连后所有客户端同时重连导致服务器压力过大
   *
   * 指数退避公式：delay = min(base * 2^attempts, maxDelay)
   * 第1次重连：1秒后
   * 第2次重连：2秒后
   * 第3次重连：4秒后
   * 第4次及以后：8秒、16秒、30秒（达到上限）
   */
  function scheduleReconnect(): void {
    // 如果已主动断开，不重连
    if (intentionalDisconnect) return

    // 计算延迟（指数退避）
    const delay = Math.min(
      RECONNECT_BASE_DELAY * Math.pow(2, reconnectAttempts),
      MAX_RECONNECT_DELAY
    )
    reconnectAttempts++

    console.log(
      `[H5 WS] 将在 ${delay / 1000} 秒后重连（第 ${reconnectAttempts} 次）`
    )

    // 清理旧的重连定时器
    if (reconnectTimer) {
      clearTimeout(reconnectTimer)
    }

    // 安排重连
    reconnectTimer = setTimeout(() => {
      console.log('[H5 WS] 正在重连...')
      connect()
    }, delay)
  }

  // ==========================================================================
  // 消息处理（事件分发）
  // ==========================================================================

  /**
   * 处理从 WebSocket 收到的消息
   *
   * 做什么：根据消息的 type 字段，调用对应的 store 方法处理
   * 为什么：不同类型的事件需要不同的处理逻辑
   *
   * H5员工端关注的事件：
   * - participant_invited: 新参与者被邀请 — 刷新参与者列表
   * - participant_joined: 参与者加入 — 刷新参与者列表
   * - participant_removed: 参与者被移除 — 刷新参与者列表
   * - participant_left: 参与者退出 — 刷新参与者列表
   * - new_message: 新消息 — 追加到消息列表
   * - pong: 心跳响应，忽略
   *
   * @param msg - WebSocket 消息对象，包含 type 和 data 字段
   */
  function handleMessage(msg: { type: string; data?: any }): void {
    const store = useConversationStore()

    switch (msg.type) {
      // ==================================================================
      // 参与者变更事件（邀请功能核心）
      // ==================================================================
      case 'participant_invited':
        // 参与者被邀请：实时刷新参与者列表
        // 做什么：用 WS 推送的 participants 数据直接更新 store
        // 为什么：比等3秒轮询更实时，被邀请人能看到最新的参与者状态
        if (msg.data?.participants) {
          store.updateParticipants(msg.data.participants)
        }
        break

      case 'participant_joined':
        // 参与者加入：实时刷新参与者列表
        if (msg.data?.participants) {
          store.updateParticipants(msg.data.participants)
        }
        break

      case 'participant_removed':
        // 参与者被移除：实时刷新参与者列表
        // 特殊：如果被移除的是当前用户，需要退出会话视图
        if (msg.data?.participants) {
          store.updateParticipants(msg.data.participants)
        }
        // 检查是否当前用户被移除
        if (msg.data?.changed) {
          const employeeStore = useEmployeeStore()
          const removedIds = msg.data.changed.map((p: any) => p.id)
          if (removedIds.includes(employeeStore.employeeId)) {
            console.log('[H5 WS] 当前用户被移除会话')
            store.handleRemovedFromConversation()
          }
        }
        break

      case 'participant_left':
        // 参与者主动退出：实时刷新参与者列表
        if (msg.data?.participants) {
          store.updateParticipants(msg.data.participants)
        }
        break

      // ==================================================================
      // 新消息事件
      // ==================================================================
      case 'new_message':
        // 新消息事件：追加到消息列表
        // 做什么：检查消息是否属于当前会话，是则追加到消息列表
        // 为什么：比3秒轮询更实时，坐席/其他参与者的回复立即可见
        if (msg.data) {
          store.handleNewMessage(msg.data)
        }
        break

      case 'pong':
        // 心跳响应，不需要处理
        break

      default:
        console.warn(`[H5 WS] 未知消息类型: ${msg.type}`)
    }
  }

  // ==========================================================================
  // 返回
  // ==========================================================================
  return {
    /** 建立 WebSocket 连接 */
    connect,
    /** 主动断开 WebSocket 连接（登出时调用） */
    disconnect,
  }
}
