//! 面板与持久化 — togglePanel/toggleMaximize/cyclePanelMode + UI 持久化(restoreUiState/persistUiState)
//!                + token 开关 + providers/skills/clearChat
//!
//! 模块级私有:
//! - 启动恢复:模块加载时 restoreUiState()(从 appSettings 读上次 UI 布局)
//! - watch appSettings.cache['df-ai-ui']:loadAll() 异步填充后再次应用 UI 布局
//!
//! 耦合:
//! - togglePanel/toggleMaximize/toggleArchivedFold/toggleSidebar 调 persistUiState

import { watch } from 'vue'
import { aiApi } from '@/api'
import { useAppSettingsStore } from '@/stores/appSettings'
import { state } from '@/stores/ai'
import { setStreaming } from './streamingGuard'
import { nextMsgId } from './aiShared'
import { t } from '@/i18n/i18n-helpers'
import type { AiMessage, AiProviderConfig } from '@/api/types'

const appSettings = useAppSettingsStore()

/** 侧栏宽度可拖拽范围(UX-2025-16)。120=窄屏友好,280=宽屏舒适 */
export const SIDEBAR_WIDTH_MIN = 120
export const SIDEBAR_WIDTH_MAX = 280
export const SIDEBAR_WIDTH_DEFAULT = 200

/** clamp 侧栏宽度到合法区间;非法/缺省值回默认(防 NaN/0/负数污染 state) */
function clampSidebarWidth(v: any): number {
  const n = typeof v === 'number' && Number.isFinite(v) ? v : SIDEBAR_WIDTH_DEFAULT
  return Math.min(SIDEBAR_WIDTH_MAX, Math.max(SIDEBAR_WIDTH_MIN, n))
}

/**
 * 将缓存的 UI 布局对象安全应用到模块级 state(仅 boolean 字段覆盖,其余忽略)。
 * restoreUiState 与 watch 回调共用,消除重复赋值。
 * detached/docked 不恢复 — 重启后分离窗口必然不存在,必须回 false
 */
function applyUiState(s: any) {
  if (!s) return
  if (typeof s.panelOpen === 'boolean') state.panelOpen = s.panelOpen
  if (typeof s.maximized === 'boolean') state.maximized = s.maximized
  if (typeof s.sidebarOpen === 'boolean') state.sidebarOpen = s.sidebarOpen
  if (typeof s.archivedCollapsed === 'boolean') state.archivedCollapsed = s.archivedCollapsed
  if (s.foldedGroups && typeof s.foldedGroups === 'object') {
    state.foldedGroups = { ...state.foldedGroups, ...s.foldedGroups }
  }
  // 侧栏宽度(UX-2025-16):数值字段,clamp 后写入(与上方 boolean 同级兜底)
  state.sidebarWidth = clampSidebarWidth(s.sidebarWidth)
}

// 启动恢复:从 appSettings(SQLite)读取上次的 UI 布局 + activeProvider(模块级单例 state)
// F-260616-10: activeProvider 恢复对齐 df-ai-active-conv 模式;loadProviders 后还会二次校验
// DB is_default(双保险:localStorage 可能与 DB 不同步时以 DB 为准)。
function restoreUiState() {
  applyUiState(appSettings.get<any>('df-ai-ui', null))
  const savedProvider = appSettings.get<string | null>('df-ai-active-provider', null)
  if (savedProvider) state.activeProvider = savedProvider
}

// 循环依赖兜底:stores/ai ↔ useAiPanel 互 import,顶层 restoreUiState 执行时 state 可能尚未
// 初始化(TDZ)。冷启动靠 appSettings 缓存为空 → applyUiState(null) 提前 return 绕过访问;
// 但 HMR 热重载 stores/ai.ts 时 appSettings 已有缓存,restoreUiState 真正访问 state 触发
// ReferenceError: Cannot access 'state' before initialization。对齐 stores/ai.ts __bindMessages
// 既存模式:try 失败则微任务延迟重试,待 state 就绪后落库恢复。
try {
  restoreUiState()
} catch {
  Promise.resolve().then(restoreUiState)
}

// loadAll() 异步填充缓存后,再次应用 UI 布局(模块级 state,watch 仅触发一次)
watch(() => appSettings.cache['df-ai-ui'], (v) => applyUiState(v as any))

/** 持久化 UI 布局到 appSettings(debounced 写 SQLite) */
export function persistUiState() {
  void appSettings.set('df-ai-ui', {
    panelOpen: state.panelOpen,
    maximized: state.maximized,
    sidebarOpen: state.sidebarOpen,
    archivedCollapsed: state.archivedCollapsed,
    foldedGroups: state.foldedGroups,
    // 侧栏宽度(UX-2025-16):拖拽 mouseup 时由 AiChat 调 persistUiState 落盘
    sidebarWidth: state.sidebarWidth,
  })
}

/**
 * 设置侧栏宽度(clamp 后写 state,不持久化)。
 * 拖拽 mousemove 高频调用,持久化由调用方在 mouseup 时一次 persistUiState() 完成,
 * 避免每像素触发 debounced SQLite 写。
 */
export function setSidebarWidth(px: number) {
  state.sidebarWidth = clampSidebarWidth(px)
}

/** 切换面板展开/收起(收起时同步退出最大化,避免 v-show 卡死 main 交互) */
function togglePanel() {
  state.panelOpen = !state.panelOpen
  // 关闭面板时同步退出最大化，避免 <main> 被 v-show 隐藏导致其他页面无法交互
  if (!state.panelOpen) state.maximized = false
  persistUiState()
}

/** 最大化/还原侧栏宽度 */
function toggleMaximize() {
  state.maximized = !state.maximized
  persistUiState()
}

/**
 * Ctrl+I 三态循环(关闭 → 分栏打开 → 最大化 → 关闭)。
 *
 * 与 togglePanel(二态开关)区分:本函数仅供全局快捷键 Ctrl+I 用;UI 按钮(侧栏 AI 按钮 /
 * TopBar 关闭钮)与审批浮层激活仍走 togglePanel,保持"开/关"二态语义不被破坏。
 *
 * 循环方向逐级增强再回关闭,贴合"要用 AI → 专注看 AI → 用完了"心智:
 *   关闭(panelOpen=false)       → 分栏打开(panelOpen=true, maximized=false)
 *   分栏打开(maximized=false)   → 最大化(maximized=true,全宽占满 main)
 *   最大化(maximized=true)      → 关闭(panelOpen=false, maximized=false)
 *
 * detached 模式 no-op:分离窗口下主窗口不渲染面板,循环无意义;分离窗口自身走系统标题栏最大化。
 */
function cyclePanelMode() {
  // 分离窗口模式:主窗口无面板,三态循环无意义(no-op;关闭分离窗口时 destroyed 钩子自恢复 panelOpen)
  if (state.detached) return
  if (!state.panelOpen) {
    state.panelOpen = true
    state.maximized = false
  } else if (!state.maximized) {
    state.maximized = true
  } else {
    state.panelOpen = false
    state.maximized = false
  }
  persistUiState()
}

// ── Providers / Skills / 清屏 ──

/** 拉取 provider 列表(用于顶部 bar 展示与切换) + 同步 DB 默认 provider 到 activeProvider */
async function loadProviders() {
  const list = await aiApi.listProviders()
  state.providers = list
  // F-260616-10: 重启后 activeProvider=null,从 DB is_default 恢复(后端链路正确,仅前端缺同步)
  // CR-34-1: 守卫加有效性检查 — savedProvider 指向已删除 provider 时残留无效 id
  if (!state.activeProvider || !list.some((p: AiProviderConfig) => p.id === state.activeProvider)) {
    const def = list.find((p: AiProviderConfig) => p.is_default)
    if (def) state.activeProvider = def.id
  }
}

/** 拉取本机技能列表(用于 `/` 联想,静默失败) */
async function loadSkills() {
  try {
    state.skills = await aiApi.listSkills()
  } catch {
    // 静默失败，不影响主流程
  }
}

/** 切换活跃 provider(后端 + 本地 state + 持久化) */
async function setProvider(providerId: string) {
  await aiApi.setProvider(providerId)
  state.activeProvider = providerId
  // F-260616-10: 持久化到 appSettings,重启后恢复(对齐 df-ai-active-conv 模式)
  void appSettings.set('df-ai-active-provider', providerId)
}

/** 清空当前会话消息(后端 + 本地 state) */
async function clearChat() {
  try {
    await aiApi.clearChat()
    state.messages = []
    state.currentText = ''
    state.pendingApprovals = []
    setStreaming(false, { reason: 'clearChat' })
  } catch (e) {
    // A2-B9(G3.2):清空失败需反馈,原裸 await 失败静默(异常进 unhandledrejection,用户无感)。
    // 后端裁决「先停 loop → DB 单事务 → 内存清」:DB 失败时内存不动(回滚一致),故此处
    // 只推错误气泡告知用户(对齐 useAiConversations.pushConvOpFail 模式),不吞异常日志。
    // 注意:不 rethrow——confirmClearChat 无 try,裸抛会进 unhandledrejection 且用户无感;
    // 错误气泡已足够反馈,且状态保持未清(与后端内存一致)。
    const msg = e instanceof Error ? e.message : String(e)
    state.messages.push({
      id: `conv-op-fail-${nextMsgId()}`,
      role: 'assistant',
      content: t('aiChat.clearError', { msg }),
      isError: true,
      timestamp: Date.now(),
    } as AiMessage)
    console.error('[AI] 清空对话失败:', e)
  }
}

export function useAiPanel() {
  return {
    togglePanel,
    toggleMaximize,
    cyclePanelMode,
    loadProviders,
    loadSkills,
    setProvider,
    clearChat,
    // UX-2025-16:侧栏宽度拖拽
    setSidebarWidth,
    persistUiState,
  }
}
