//! 生命周期事件域 — handleLifecycleEvent + cleanupTerminatedConversation
//!
//! 处理:AiCompleted / AiError / AiHelpRequired + 上下文生命周期事件(AiCompressing/
//! AiManualCompressed/AiAutoCompressed/AiContextCleared)。
//! 依赖方向:本模块 → aiShared / useAiPendingState(叶子)/ useAiStream / streamingGuard /
//! useAiConversations,不导入任何同级子 composable。

import { emit } from '@tauri-apps/api/event'
import { useAppSettingsStore } from '@/stores/appSettings'
import { state } from '@/stores/ai'
import { t } from '@/i18n/i18n-helpers'
import { nextMsgId, convStates, clearApprovalTimer, flushCurrentText, clearAllToolSlowTimers, friendlyError, notifyConversationChanged } from './aiShared'
import { clearTextIdleTimer, pendingMaxRounds, pendingHelp, pendingDirAuths } from './useAiPendingState'
import { clearStreamWatchdog } from './useAiStream'
import { setStreaming } from './streamingGuard'
import { loadConversations } from './useAiConversations'
import type { AiChatEvent, AiMessage, MessageId } from '@/api/types'

const appSettings = useAppSettingsStore()

// 上下文管理生命周期事件回调(useAiContext.initContextListener 注入,本域统一分发)
export interface ContextCallbacks {
  /** 压缩开始(AiCompressing):置 loading=true */
  onCompressing?: () => void
  /** 手动压缩成功(AiManualCompressed):置 loading=false + toast + 回填摘要 */
  onManualCompressed?: (conversationId: string, summary: string) => void
  /** 自动压缩成功(AiAutoCompressed):静默置 loading=false(不弹 toast) */
  onAutoCompressed?: () => void
  /** 上下文已清空(AiContextCleared):toast + 刷新消息列表 */
  onCleared?: (conversationId: string) => void
  /** 错误(AiError,含压缩失败):仅在 loading 时置 false + toast */
  onError?: (conversationId: string, message: string) => void
}
let _contextCallbacks: ContextCallbacks = {}

/** 注入上下文生命周期回调(useAiContext.initContextListener 调用) */
export function setContextCallbacks(callbacks: ContextCallbacks): void {
  _contextCallbacks = callbacks
}

/** token 用量展示开关(读 appSettings,与 Settings.vue 共享 key `df-show-token-usage`) */
function isShowTokenUsage(): boolean {
  return appSettings.get<boolean>('df-show-token-usage', false)
}

/**
 * 公共会话终止收尾(AiCompleted/AiError/AiHelpRequired 共用):清看门狗/计时器/流式态/挂起。
 *
 * 语义差异(调用方自行处理):
 * - AiCompleted: 调用后追加 incomplete 气泡 / token 用量 / 队列 drain
 * - AiError:    调用后追加错误气泡 + 清 queue
 * - AiHelpRequired: 调用后翻 pendingHelp 驱动求助卡
 */
function cleanupTerminatedConversation(convId: string, reason: 'AiCompleted' | 'AiError' | 'AiHelpRequired') {
  clearStreamWatchdog(convId || undefined)
  clearAllToolSlowTimers() // 整轮/错误/求助结束清全部工具慢执行计时器与已提示集合
  clearTextIdleTimer() // 清文本空闲定时器 + 置 textIdle=true(整轮结束不该有活跃信号残留)
  flushCurrentText()
  state.currentText = ''
  // 清「重试中」过渡气泡(成功后残留根治;AiStreamRetry 推的 retry 气泡是过渡态,终态不清会永久残留)
  for (let i = state.messages.length - 1; i >= 0; i--) {
    const m = state.messages[i]
    if (m.isError && typeof m.content === 'string' && m.content.includes('正在重试(')) {
      state.messages.splice(i, 1)
    }
  }
  setStreaming(false, { convId: convId || null, reason })
  state.agentRound = 0 // 结束/中断/求助,复位轮次(隐藏进度条)
  // convStates 收敛:AiError 保留 error 项(停止按钮显「重试」态),其余删项回 idle
  if (reason === 'AiError') {
    if (convId) convStates.set(convId, 'error')
  } else {
    convStates.delete(convId)
  }
  // per-conv 挂起仅清本 conv(避免清错其他会话的挂起)
  if (pendingMaxRounds.value && pendingMaxRounds.value === convId) {
    pendingMaxRounds.value = null
  }
  if (pendingHelp.value && pendingHelp.value.conversationId === convId) {
    pendingHelp.value = null
  }
  // 路径授权挂起仅清本 conv(终止只清本会话弹窗,不连累并发会话;无归属旧项保守保留)
  pendingDirAuths.value = pendingDirAuths.value.filter(p => !p.conversationId || p.conversationId !== convId)
  // 审批收尾仅清目标 conv 的待审批项 + 对称清其超时计时器(其他会话的审批卡/计时器不受影响;
  // conversationId 缺失的旧项无法归属,保守保留防误清正在审批的卡)
  const removedApprovals = state.pendingApprovals.filter(p => p.conversationId === convId)
  state.pendingApprovals = state.pendingApprovals.filter(p => !p.conversationId || p.conversationId !== convId)
  for (const p of removedApprovals) clearApprovalTimer(p.id)
  // 清分离窗口生成态快照(per-conv key,清本会话;兼容旧单 key)
  if (convId) {
    localStorage.removeItem(`df-ai-gen-${convId}`)
    localStorage.removeItem(`df-ai-text-${convId}`)
  }
  localStorage.removeItem('df-ai-gen')
  localStorage.removeItem('df-ai-text')
}

/** lifecycle 域:整轮收尾(AiCompleted)与异常中断(AiError)。返回 true=已处理。 */
export function handleLifecycleEvent(event: AiChatEvent): boolean {
  switch (event.type) {
    case 'AiCompleted': {
      console.warn('[FE-AICOMPLETED] 收到 conv=', event.conversation_id, 'streaming=', state.streaming)
      cleanupTerminatedConversation(event.conversation_id || '', 'AiCompleted')
      console.warn('[FE-AICOMPLETED] cleanup 后 streaming=', state.streaming)
      // 流中途失败保文:后端 emit AiCompleted(incomplete=true),前端追加系统提示气泡
      if (event.incomplete) {
        state.messages.push({
          id: `incomplete-${nextMsgId()}` as MessageId,
          role: 'assistant',
          content: t('ai.responseIncomplete'),
          timestamp: Date.now(),
        })
      }
      // 直接从事件更新 pinned_goals,不等 loadConversations 异步刷新
      if (event.pinned_goals && state.activeConversationId) {
        const conv = state.conversations.find(c => c.id === state.activeConversationId)
        if (conv) conv.pinned_goals = event.pinned_goals
      }
      void loadConversations()
      // token 用量记录(开关开时):lastTokenUsage 供当前回复展示,convTokenTotal 累加对话总量
      if (isShowTokenUsage()) {
        state.lastTokenUsage = {
          prompt: event.prompt_tokens,
          completion: event.completion_tokens,
          total: event.total_tokens,
          cache_hit: event.prompt_cache_hit_tokens,
          cache_miss: event.prompt_cache_miss_tokens,
          reasoning: event.reasoning_tokens,
        }
        if (state.convTokenTotal) {
          state.convTokenTotal.prompt += event.prompt_tokens
          state.convTokenTotal.completion += event.completion_tokens
          state.convTokenTotal.total += event.total_tokens
        } else {
          state.convTokenTotal = { prompt: event.prompt_tokens, completion: event.completion_tokens, total: event.total_tokens }
        }
        // 每轮 token 写入最后一条 AI 消息,供 MessageList 逐条显示
        for (let i = state.messages.length - 1; i >= 0; i--) {
          const m = state.messages[i]
          if (m.role === 'assistant' && !m.isError) {
            m.tokenUsage = {
              prompt: event.prompt_tokens,
              completion: event.completion_tokens,
              cache_hit: event.prompt_cache_hit_tokens,
              cache_miss: event.prompt_cache_miss_tokens,
              reasoning: event.reasoning_tokens,
              is_estimated: event.is_estimated,
            }
            m.prompt_cache_hit_tokens = event.prompt_cache_hit_tokens
            m.prompt_cache_miss_tokens = event.prompt_cache_miss_tokens
            m.reasoning_tokens = event.reasoning_tokens
            break
          }
        }
      }
      notifyConversationChanged()
      // 队列续发:当前完成后自动发下一条(经事件总线桥接,避免 import useAiSend 循环依赖)
      emit('ai-drain-queue', { conversationId: event.conversation_id })
      return true
    }

    case 'AiError': {
      cleanupTerminatedConversation(event.conversation_id || '', 'AiError')
      // 只清除出错会话的队列项,不误伤其他会话的排队消息
      if (event.conversation_id) {
        state.queue = state.queue.filter(q => q.conversationId !== event.conversation_id)
      } else {
        state.queue = []
      }
      state.messages.push({
        id: `err-${nextMsgId()}` as MessageId,
        role: 'assistant',
        content: friendlyError(event.error),
        isError: true,
        errorType: event.error_type,
        timestamp: Date.now(),
      } as AiMessage)
      // AiError 也用于压缩 IPC 失败:通知上下文回调复位 loading + 弹错误 toast(内部判 isCompressing 守卫)
      _contextCallbacks.onError?.(event.conversation_id || '', event.error)
      return true
    }

    case 'AiHelpRequired': {
      // 求助协议:后端断路器熔断已 guard.reset(loop 终止)。按终止态收尾但不创建错误气泡,
      // 改为翻 pendingHelp 驱动求助卡(reason + options 按钮供用户选)。
      cleanupTerminatedConversation(event.conversation_id || '', 'AiHelpRequired')
      // 用 convId 兜底(后端必带,无时默认当前活跃会话,优于丢卡片)
      pendingHelp.value = {
        reason: event.reason,
        context: event.context,
        options: event.options,
        conversationId: event.conversation_id || state.activeConversationId || null,
      }
      void loadConversations()
      notifyConversationChanged()
      return true
    }

    case 'AiCompressing':
      _contextCallbacks.onCompressing?.()
      return true

    case 'AiManualCompressed': {
      // 手动 IPC 压缩:复位 loading + 弹 toast + 刷新视图回填摘要
      _contextCallbacks.onManualCompressed?.(event.conversation_id || '', event.summary || '')
      return true
    }

    case 'AiAutoCompressed': {
      // loop 自动压缩对桌面静默——仅复位 loading(不弹 toast、不打断 LLM 流)
      _contextCallbacks.onAutoCompressed?.()
      return true
    }

    case 'AiContextCleared':
      _contextCallbacks.onCleared?.(event.conversation_id || '')
      return true

    default:
      return false
  }
}
