/* AI_CHANGELOG
| 日期 | Prompt | 变更 |
|------|--------|------|
| 2026-03-23 | 角色路由+页面权限守卫 | onShow 添加 checkPageAccess 权限守卫 |
*/
import { checkPageAccess } from '../../utils/auth-guard'
// pages/chat/chat.ts — AI 对话页
// CHANGE 2026-03-20 | RNS1.4 T8.1: 多入口参数路由，替换 mock 为真实 API
// CHANGE 2026-03-20 | RNS1.4 T8.2: 替换 simulateStreamOutput 为真实 SSE 连接
import { fetchChatMessages, fetchChatMessagesByContext } from '../../services/api'
import { formatRelativeTime, formatIMTime, shouldShowTimeDivider } from '../../utils/time'
import { API_BASE } from '../../utils/config'

/** API 返回的消息项类型 */
interface ApiMessageItem {
  id: number
  role: 'user' | 'assistant'
  content: string
  createdAt: string
  referenceCard?: {
    type: 'customer' | 'record'
    title: string
    summary: string
    data: Record<string, string>
  }
}

/** 将 referenceCard.data (Record<string,string>) 转为数组供 WXML wx:for 渲染 */
function toDataList(data?: Record<string, string>): Array<{ key: string; value: string }> {
  if (!data) return []
  return Object.keys(data).map((k) => ({ key: k, value: data[k] }))
}

/** 为消息列表补充展示字段（时间分割线、IM时间、引用卡片）*/
function enrichMessages(msgs: ApiMessageItem[]) {
  return msgs.map((m, i) => ({
    id: String(m.id),
    role: m.role,
    content: m.content,
    timestamp: m.createdAt,
    timeLabel: formatRelativeTime(m.createdAt),
    imTimeLabel: formatIMTime(m.createdAt),
    showTimeDivider: shouldShowTimeDivider(
      i === 0 ? null : msgs[i - 1].createdAt,
      m.createdAt,
    ),
    referenceCard: m.referenceCard
      ? { ...m.referenceCard, dataList: toDataList(m.referenceCard.data) }
      : undefined,
  }))
}

/** 从 Storage 获取 access_token（与 request.ts 保持一致） */
function getAccessToken(): string | undefined {
  const app = getApp<IAppOption>()
  return app.globalData.token || wx.getStorageSync('token') || undefined
}

/**
 * SSE 行缓冲解析器
 * 微信 onChunkReceived 返回的 ArrayBuffer 可能跨事件边界切割，
 * 需要缓冲不完整的行，只处理完整的 SSE 事件。
 */
class SSEParser {
  private buffer = ''
  private currentEvent = ''
  private currentData = ''

  constructor(
    private onMessage: (token: string) => void,
    private onDone: (messageId: number, createdAt: string) => void,
    private onError: (message: string) => void,
  ) {}

  /** 将 ArrayBuffer 解码为 UTF-8 字符串（兼容微信环境） */
  private decodeChunk(chunk: ArrayBuffer): string {
    // 微信基础库支持 TextDecoder（2.19.2+）
    if (typeof TextDecoder !== 'undefined') {
      return new TextDecoder('utf-8').decode(chunk)
    }
    // 降级：手动解码 UTF-8 字节
    const bytes = new Uint8Array(chunk)
    let str = ''
    for (let i = 0; i < bytes.length; i++) {
      str += String.fromCharCode(bytes[i])
    }
    try {
      return decodeURIComponent(escape(str))
    } catch {
      return str
    }
  }

  /** 接收一个 chunk 并解析完整的 SSE 事件 */
  feed(chunk: ArrayBuffer): void {
    this.buffer += this.decodeChunk(chunk)
    const lines = this.buffer.split('\n')
    // 最后一个元素可能是不完整的行，保留在 buffer 中
    this.buffer = lines.pop() || ''

    for (const line of lines) {
      const trimmed = line.replace(/\r$/, '')
      if (trimmed === '') {
        // 空行 = 事件分隔符，派发当前事件
        this.dispatch()
      } else if (trimmed.startsWith('event:')) {
        this.currentEvent = trimmed.slice(6).trim()
      } else if (trimmed.startsWith('data:')) {
        this.currentData = trimmed.slice(5).trim()
      }
      // 忽略其他行（如 id:、retry:、注释等）
    }
  }

  private dispatch(): void {
    if (!this.currentEvent || !this.currentData) {
      this.currentEvent = ''
      this.currentData = ''
      return
    }

    try {
      const data = JSON.parse(this.currentData)
      switch (this.currentEvent) {
        case 'message':
          if (data.token != null) this.onMessage(data.token)
          break
        case 'done':
          this.onDone(data.messageId, data.createdAt)
          break
        case 'error':
          this.onError(data.message || 'AI 服务暂时不可用')
          break
      }
    } catch {
      // JSON 解析失败，忽略此事件
    }

    this.currentEvent = ''
    this.currentData = ''
  }
}

/** 展示用消息类型 */
type DisplayMessage = {
  id: string
  role: 'user' | 'assistant'
  content: string
  timestamp: string
  timeLabel?: string
  imTimeLabel?: string
  showTimeDivider?: boolean
  referenceCard?: {
    type: string
    title: string
    summary: string
    data: Record<string, string>
    dataList?: Array<{ key: string; value: string }>
  }
}

Page({
  data: {
    /** 页面状态 */
    pageState: 'loading' as 'loading' | 'empty' | 'normal' | 'error',
    /** 状态栏高度 */
    statusBarHeight: 0,
    /** 消息列表 */
    messages: [] as DisplayMessage[],
    /** 输入框内容 */
    inputText: '',
    /** AI 正在流式回复 */
    isStreaming: false,
    /** 流式输出中的内容 */
    streamingContent: '',
    /** 滚动锚点 */
    scrollToId: '',
    /** 页面顶部引用卡片（从其他页面跳转时） */
    referenceCard: null as { title: string; summary: string } | null,
    /** 当前对话 ID（后端返回，用于发送消息） */
    chatId: '',
    /** 客户 ID（兼容旧逻辑） */
    customerId: '',
    /** 输入栏距底部距离（软键盘弹出时动态调整）*/
    inputBarBottom: 0,
    /** 来源页面标识（看板入口） */
    sourcePage: '',
    /** 看板筛选参数 */
    pageFilters: {} as Record<string, string>,
  },

  /** 消息计数器，用于生成唯一 ID */
  _msgCounter: 0,

  /** 当前 SSE 请求任务（用于中断/重试） */
  _sseTask: null as WechatMiniprogram.RequestTask | null,

  /** 最后一次发送的用户消息内容（用于重试） */
  _lastUserContent: '',

  /** SSE 断线重试次数 */
  _sseRetryCount: 0,

  /** SSE 最大自动重试次数 */
  _SSE_MAX_RETRY: 2,

  onShow() {
    // 权限守卫：检查登录状态、账号禁用、角色权限
    checkPageAccess('pages/chat/chat')
  },

  // CHANGE 2026-03-20 | RNS1.4 T8.1: 多入口参数路由
  // 优先级：historyId → taskId → customerId → coachId → general
  onLoad(options) {
    const sysInfo = wx.getWindowInfo()
    this.setData({
      statusBarHeight: sysInfo.statusBarHeight || 44,
    })

    if (options?.historyId) {
      // 从 chat-history 跳转：直接用 historyId 作为 chatId 加载历史消息
      this.setData({ chatId: options.historyId })
      this.loadMessages(options.historyId)
    } else if (options?.taskId) {
      // 从 task-detail 跳转：同一 taskId 始终复用同一对话（无时限）
      this.loadMessagesByContext('task', options.taskId)
    } else if (options?.customerId) {
      // 从 customer-detail 跳转：≤ 3 天复用、> 3 天新建
      this.setData({ customerId: options.customerId })
      this.loadMessagesByContext('customer', options.customerId)
    } else if (options?.coachId) {
      // 从 coach-detail 跳转：≤ 3 天复用、> 3 天新建
      this.loadMessagesByContext('coach', options.coachId)
    } else if (options?.sourcePage) {
      // 看板类入口：保存来源页面和筛选参数
      // Phase 2.3：优先解析 options.pageFilters（ai-float-button 传入的 JSON 字符串），
      // 回退到单独键（旧入口兼容：timeDimension / areaFilter 等）
      const pageFilters: Record<string, string> = {}
      if (options.pageFilters) {
        try {
          const parsed = JSON.parse(decodeURIComponent(options.pageFilters))
          if (parsed && typeof parsed === 'object') {
            for (const k of Object.keys(parsed)) {
              const v = parsed[k]
              if (v != null) pageFilters[k] = String(v)
            }
          }
        } catch {
          // JSON 解析失败忽略，回退到单键读取
        }
      }
      if (Object.keys(pageFilters).length === 0) {
        const filterKeys = ['timeDimension', 'areaFilter', 'dimension', 'typeFilter', 'projectFilter']
        for (const key of filterKeys) {
          if (options[key]) pageFilters[key] = options[key]
        }
      }
      this.setData({ sourcePage: options.sourcePage, pageFilters })
      this.loadMessagesByContext(options.sourcePage, '')
    } else {
      // 无参数入口：始终新建通用对话
      this.loadMessagesByContext('general', '')
    }
  },

  /** 通过 chatId 加载历史消息（historyId 入口） */
  async loadMessages(chatId: string) {
    this.setData({ pageState: 'loading' })
    wx.showLoading({ title: '加载中...', mask: true })
    try {
      const res = await fetchChatMessages(chatId)
      this.setData({ chatId: String(res.chatId) })
      const messages = enrichMessages(res.items as ApiMessageItem[])
      this._msgCounter = messages.length
      this.setData({
        pageState: messages.length === 0 ? 'empty' : 'normal',
        messages,
      })
      this.scrollToBottom()
    } catch {
      this.setData({ pageState: 'error' })
    } finally {
      wx.hideLoading()
    }
  },

  /** 通过上下文类型加载消息（task/customer/coach/general 入口） */
  async loadMessagesByContext(contextType: string, contextId: string) {
    this.setData({ pageState: 'loading' })
    wx.showLoading({ title: '加载中...', mask: true })
    try {
      const res = await fetchChatMessagesByContext(contextType, contextId)
      // 缓存后端返回的 chatId，后续发送消息使用
      this.setData({ chatId: String(res.chatId) })
      const messages = enrichMessages(res.items as ApiMessageItem[])
      this._msgCounter = messages.length

      // customerId 入口时显示引用卡片
      const referenceCard = contextType === 'customer' && contextId
        ? { title: '客户详情', summary: `正在查看客户 ${contextId} 的相关信息` }
        : null

      const isEmpty = messages.length === 0 && !referenceCard
      this.setData({
        pageState: isEmpty ? 'empty' : 'normal',
        messages,
        referenceCard,
      })
      this.scrollToBottom()
    } catch {
      this.setData({ pageState: 'error' })
    } finally {
      wx.hideLoading()
    }
  },

  /** 返回上一页 */
  onBack() {
    // 中断进行中的 SSE 连接
    if (this._sseTask) {
      this._sseTask.abort()
      this._sseTask = null
    }
    wx.navigateBack()
  },

  /** 页面卸载时清理 SSE 连接 */
  onUnload() {
    if (this._sseTask) {
      this._sseTask.abort()
      this._sseTask = null
    }
  },

  /** 重试加载 */
  onRetry() {
    if (this.data.chatId) {
      this.loadMessages(this.data.chatId)
    } else {
      this.loadMessagesByContext('general', '')
    }
  },

  /** 软键盘弹出：输入栏上移至键盘顶部 */
  onInputFocus(e: WechatMiniprogram.InputFocus) {
    const keyboardHeight = e.detail.height || 0
    this.setData({ inputBarBottom: keyboardHeight })
    setTimeout(() => this.scrollToBottom(), 120)
  },

  /** 软键盘收起：输入栏归位 */
  onInputBlur() {
    this.setData({ inputBarBottom: 0 })
  },

  /** 输入框内容变化 */
  onInputChange(e: WechatMiniprogram.Input) {
    this.setData({ inputText: e.detail.value })
  },

  /** 发送消息 */
  onSendMessage() {
    const text = this.data.inputText.trim()
    if (!text || this.data.isStreaming) return

    this._msgCounter++
    const now = new Date().toISOString()
    const prevMsgs = this.data.messages
    const prevTs = prevMsgs.length > 0 ? prevMsgs[prevMsgs.length - 1].timestamp : null
    const userMsg: DisplayMessage = {
      id: `msg-user-${this._msgCounter}`,
      role: 'user',
      content: text,
      timestamp: now,
      timeLabel: '刚刚',
      imTimeLabel: formatIMTime(now),
      showTimeDivider: shouldShowTimeDivider(prevTs, now),
    }

    const messages = [...this.data.messages, userMsg]
    this.setData({
      messages,
      inputText: '',
      pageState: 'normal',
    })

    this.scrollToBottom()

    // 保存用户消息内容，用于 SSE 发送和重试
    this._lastUserContent = text

    // CHANGE 2026-03-20 | T8.2: 使用真实 SSE 替代 mock
    setTimeout(() => {
      this.triggerAIReply()
    }, 300)
  },

  // CHANGE 2026-03-20 | T8.2: 真实 SSE 流式连接替代 mock
  /** 触发 AI 流式回复（通过 SSE 连接后端 /api/xcx/chat/stream） */
  triggerAIReply() {
    const chatId = this.data.chatId
    const content = this._lastUserContent
    if (!chatId || !content) return

    this._msgCounter++
    const aiMsgId = `msg-ai-${this._msgCounter}`
    const aiNow = new Date().toISOString()
    const prevMsgs = this.data.messages
    const prevTs = prevMsgs.length > 0 ? prevMsgs[prevMsgs.length - 1].timestamp : null
    const aiMsg: DisplayMessage = {
      id: aiMsgId,
      role: 'assistant',
      content: '',
      timestamp: aiNow,
      timeLabel: '刚刚',
      imTimeLabel: formatIMTime(aiNow),
      showTimeDivider: shouldShowTimeDivider(prevTs, aiNow),
    }

    const messages = [...this.data.messages, aiMsg]
    this.setData({
      messages,
      isStreaming: true,
      streamingContent: '',
    })
    this.scrollToBottom()

    const aiIndex = messages.length - 1
    let fullContent = ''

    const parser = new SSEParser(
      // onMessage: 逐 token 追加
      (token: string) => {
        fullContent += token
        const key = `messages[${aiIndex}].content`
        this.setData({
          [key]: fullContent,
          streamingContent: fullContent,
        })
        this.scrollToBottom()
      },
      // onDone: 流结束，更新消息 ID 和时间
      (messageId: number, createdAt: string) => {
        this._sseRetryCount = 0
        this.setData({
          [`messages[${aiIndex}].id`]: String(messageId),
          [`messages[${aiIndex}].timestamp`]: createdAt,
          [`messages[${aiIndex}].timeLabel`]: formatRelativeTime(createdAt),
          [`messages[${aiIndex}].imTimeLabel`]: formatIMTime(createdAt),
          isStreaming: false,
          streamingContent: '',
        })
        this._sseTask = null
      },
      // onError: 显示错误
      (message: string) => {
        // 将错误信息作为 AI 回复内容展示
        const errorContent = fullContent || message
        this.setData({
          [`messages[${aiIndex}].content`]: errorContent,
          isStreaming: false,
          streamingContent: '',
        })
        this._sseTask = null
        wx.showToast({ title: message, icon: 'none', duration: 3000 })
      },
    )

    // 构建认证 header
    const token = getAccessToken()
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
    }
    if (token) {
      headers['Authorization'] = `Bearer ${token}`
    }

    // 使用 wx.request + enableChunked 接收 SSE
    // enableChunked 和 onChunkReceived 在基础库 2.20.2+ 可用，
    // 当前 typings 未包含，使用 as any 绕过类型检查
    const requestTask = wx.request({
      url: `${API_BASE}/api/xcx/chat/stream`,
      method: 'POST',
      data: {
        chatId: Number(chatId),
        content,
        ...(this.data.sourcePage ? {
          sourcePage: this.data.sourcePage,
          pageContext: this.data.pageFilters,
        } : {}),
      },
      header: headers,
      enableChunked: true,
      responseType: 'arraybuffer',
      success: () => {
        // enableChunked 模式下 success 在连接关闭时触发
        // 如果此时仍在 streaming 状态，说明连接正常关闭但未收到 done 事件
        if (this.data.isStreaming) {
          this.setData({ isStreaming: false, streamingContent: '' })
          this._sseTask = null
        }
      },
      fail: () => {
        // 网络错误或连接中断：无内容时指数退避重连
        this._sseTask = null
        if (!this.data.isStreaming) return
        if (fullContent === '' && this._sseRetryCount < this._SSE_MAX_RETRY) {
          this._sseRetryCount++
          const delay = (2 ** this._sseRetryCount) * 1000
          wx.showToast({ title: `重连中 ${this._sseRetryCount}/${this._SSE_MAX_RETRY}...`, icon: 'loading', duration: delay })
          this.setData({
            messages: this.data.messages.slice(0, aiIndex),
            isStreaming: false,
            streamingContent: '',
          })
          setTimeout(() => { this.triggerAIReply(chatId, content) }, delay)
        } else {
          const errorContent = fullContent || '连接中断，请重试'
          this.setData({
            [`messages[${aiIndex}].content`]: errorContent,
            isStreaming: false,
            streamingContent: '',
          })
          wx.showToast({ title: '连接中断', icon: 'none', duration: 3000 })
        }
      },
    } as WechatMiniprogram.RequestOption)

    // 监听 chunked 数据（SSE 事件流）
    // onChunkReceived 在 typings 中未定义，使用 as any
    ;(requestTask as any).onChunkReceived((res: { data: ArrayBuffer }) => {
      parser.feed(res.data)
    })

    this._sseTask = requestTask
  },

  /** 滚动到底部 */
  scrollToBottom() {
    setTimeout(() => {
      this.setData({ scrollToId: '' })
      setTimeout(() => {
        this.setData({ scrollToId: 'scroll-bottom' })
      }, 50)
    }, 50)
  },

  /** 点击引用卡片：跳转到对应详情页（Phase 2.1） */
  onRefCardTap(e: WechatMiniprogram.BaseEvent & { currentTarget: { dataset: { link?: string } } }) {
    const link = e.currentTarget?.dataset?.link
    if (!link || typeof link !== 'string') {
      return
    }
    wx.navigateTo({
      url: link,
      fail: (err) => {
        console.error('跳转引用详情失败', err)
        wx.showToast({ title: '跳转失败', icon: 'none' })
      },
    })
  },
})
