'use client'

import { useCallback, useRef, useState } from 'react'
import type { PendingAttachment, StreamingAttachment } from '@/lib/h5/types'

/**
 * useH5ChatStream — H5 对话页 SSE 流式收发 hook。
 *
 * 从 h5-client-app.tsx 的 handleSend 抽离：发起 /api/h5/chat、解析 SSE 帧、
 * 维护流式 state（streaming / streamingText / streamingError / streamingAttachments）。
 *
 * 时序硬约束（done 分支，必须原样保留）：
 *   收到 done 帧时先 `await onDone()`（组件同步持久化消息），再清流式 state，
 *   随后 break 使 finally 的 setStreaming(false) 与清空动作落在同一无 await 同步段，
 *   React 批处理为单次 re-render → 流式气泡消失的同一帧持久化消息已就位（无缝替换）。
 *   因此组件的 onDone 必须在内部 await 完 loadMessages / loadSessions 后才 resolve。
 */

interface UseH5ChatStreamOptions {
  /** 收到 done 帧时触发；组件在此 await 完消息 / session 同步后返回，hook 才清流式 state。 */
  onDone: () => Promise<void>
  /** 401 触发；组件通常 setExpired(true)。 */
  onUnauthorized: () => void
}

export interface H5StreamParams {
  sessionId: string | null
  message: string
  attachments: PendingAttachment[]
}

/** 工具名 → 用户可感知的活动状态文案（不暴露工具细节，只说"在做什么"）。 */
function mapToolToText(name: string): string {
  const n = name.toLowerCase()
  if (n === 'read') return '正在读取文件...'
  if (n === 'grep' || n === 'glob' || n === 'ls') return '正在查找内容...'
  if (n === 'websearch' || n === 'webfetch' || n.includes('web')) return '正在搜索网页...'
  if (n === 'write' || n === 'edit' || n === 'multiedit') return '正在修改文件...'
  if (n === 'bash') return '正在执行操作...'
  if (n === 'agent' || n === 'task') return '正在处理子任务...'
  return '正在处理...'
}

export function useH5ChatStream({ onDone, onUnauthorized }: UseH5ChatStreamOptions) {
  const [streaming, setStreaming] = useState(false)
  const [streamingText, setStreamingText] = useState('')
  const [streamingError, setStreamingError] = useState<string | null>(null)
  const [streamingAttachments, setStreamingAttachments] = useState<StreamingAttachment[]>([])
  const [streamingActivity, setStreamingActivity] = useState<string | null>('正在思考...')

  const abortRef = useRef<AbortController | null>(null)
  // 当前流式所属 session，供 stop() 调 /api/h5/chat/stop 主动停止引擎
  const streamingSessionRef = useRef<string | null>(null)
  // ref 持有最新回调，避免 stream 闭包陈旧（onDone 依赖组件 currentSessionId 等易变 state）。
  const onDoneRef = useRef(onDone)
  onDoneRef.current = onDone
  const onUnauthorizedRef = useRef(onUnauthorized)
  onUnauthorizedRef.current = onUnauthorized

  const stream = useCallback(async ({ sessionId, message, attachments }: H5StreamParams) => {
    setStreaming(true)
    setStreamingText('')
    setStreamingError(null)
    setStreamingActivity('正在思考...')
    setStreamingAttachments([])
    streamingSessionRef.current = sessionId ?? null

    const controller = new AbortController()
    abortRef.current = controller

    try {
      const res = await fetch('/api/h5/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          sessionId: sessionId ?? undefined,
          message,
          attachments: attachments.length > 0
            ? attachments.map((a) => ({
                name: a.name,
                filename: a.filename,
                mimeType: a.mimeType,
                tier: a.tier,
              }))
            : undefined,
        }),
        signal: controller.signal,
      })

      if (res.status === 401) {
        onUnauthorizedRef.current()
        setStreaming(false)
        return
      }

      if (!res.ok || !res.body) {
        setStreaming(false)
        return
      }

      const reader = res.body.getReader()
      const decoder = new TextDecoder()
      let buffer = ''
      let assistantText = ''

      let streamFinished = false
      for (;;) {
        const { done, value } = await reader.read()
        if (done) break
        buffer += decoder.decode(value, { stream: true })

        // 解析 SSE 事件（按 \n\n 分隔）
        const events = buffer.split('\n\n')
        buffer = events.pop() ?? ''

        for (const evt of events) {
          const dataLine = evt.split('\n').find((l) => l.startsWith('data: '))
          if (!dataLine) continue
          const jsonStr = dataLine.slice(6)
          try {
            const data = JSON.parse(jsonStr) as Record<string, unknown>
            const type = data.type as string
            if (type === 'text_delta') {
              assistantText += data.text as string
              setStreamingText(assistantText)
              setStreamingActivity(null)
            } else if (type === 'thinking_start') {
              setStreamingActivity('正在思考...')
            } else if (type === 'tool_use_start') {
              setStreamingActivity(mapToolToText(data.name as string))
            } else if (type === 'done') {
              // 先 await 组件的消息同步（持久化 assistant 消息落库后的 loadMessages），
              // 再清流式 state——保证清空动作与下方 finally 的 setStreaming(false) 落在
              // 同一个无 await 的同步段内 → React 批处理为单次 re-render：
              // 流式气泡消失的同一帧持久化消息已就位，实现无缝替换。
              await onDoneRef.current()
              setStreamingText('')
              setStreamingError(null)
              setStreamingAttachments([])
              streamFinished = true
              break
            } else if (type === 'error') {
              setStreamingError((data.error as string) || 'Unknown error')
            } else if (type === 'attachment') {
              // 后端检测到 AI 产出的文件（MEDIA: 协议或 tool_use 启发式），
              // 立即把真实可下载卡片推入流式气泡，不等 done。
              const att = data.attachment as StreamingAttachment | undefined
              if (att?.url && att.name) {
                setStreamingAttachments((prev) => [...prev, att])
              }
            }
            // tool_result / tool_raw_result / tool_progress / thinking_delta /
            // agent_content 等不消费：H5 端只展示 markdown 文本 + 活动状态行。
          } catch {
            // ignore parse error
          }
        }
        if (streamFinished) break
      }
    } catch {
      // abort 或网络错误
    } finally {
      setStreaming(false)
      abortRef.current = null
      streamingSessionRef.current = null
    }
  }, [])

  const stop = useCallback(() => {
    // 主动停止：通知后端 abort 引擎（SDK query），再断开本地 SSE 流。
    // 区别于「被动断开」（关页面）：被动断开后引擎会继续后台跑完落库。
    const sid = streamingSessionRef.current
    if (sid) {
      void fetch('/api/h5/chat/stop', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sessionId: sid }),
      }).catch(() => { /* fire-and-forget：让后端 abort 引擎，不等结果 */ })
    }
    // 带 reason：避免 dev 模式 "signal is aborted without reason" 报错
    // （stream 的 catch 仍会吞掉对应的 AbortError，功能不受影响）
    abortRef.current?.abort(new DOMException('User stopped', 'AbortError'))
    setStreaming(false)
  }, [])

  return { streaming, streamingText, streamingError, streamingAttachments, streamingActivity, stream, stop }
}
