import { useCallback, useEffect, useState } from 'react'
import {
  agentsBuiltinList,
  agentsCreate,
  agentsDelete,
  agentsList,
  agentsSetBuiltinModel,
  agentsSetModel,
  apiInjectActiveModels,
  apiInjectStatus,
  getConfig,
  revealInExplorer
} from '../ipc'
import type { AgentInfo, BuiltinAgentInfo, ModelInfo } from '../types'
import { ConfirmDialog, Modal } from './Modal'
import { IconPlus } from './Icons'

/** 官方模式下的默认候选（继承/官方三别名） */
const OFFICIAL_CANDIDATES = ['sonnet', 'opus', 'haiku']
/** 模型下拉中「自定义模型 ID…」的占位值 */
const CUSTOM_MODEL = '__custom__'

interface ModelSelectProps {
  value: string
  candidateIds: string[]
  candidateLabel: (id: string) => string
  onAssign: (model: string) => void
  title?: string
}

/** 模型选择下拉：候选清单 + 「自定义模型 ID…」；当前值不在候选中时保留显示并警示 */
function ModelSelect({ value, candidateIds, candidateLabel, onAssign, title }: ModelSelectProps) {
  const [customMode, setCustomMode] = useState(false)
  const [customText, setCustomText] = useState('')
  const stale = value !== '' && !candidateIds.includes(value)

  const commitCustom = () => {
    const v = customText.trim()
    if (!v) return
    onAssign(v)
    setCustomMode(false)
    setCustomText('')
  }

  if (customMode) {
    return (
      <span style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
        <input
          className="input"
          style={{ width: 180, fontFamily: 'var(--font-mono)' }}
          placeholder="输入模型 ID"
          value={customText}
          autoFocus
          onChange={(e) => setCustomText(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') commitCustom()
            if (e.key === 'Escape') setCustomMode(false)
          }}
        />
        <button className="btn small primary" disabled={!customText.trim()} onClick={commitCustom}>
          确定
        </button>
        <button className="btn small" onClick={() => setCustomMode(false)}>
          取消
        </button>
      </span>
    )
  }

  return (
    <span style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
      <select
        className="select"
        style={{ minWidth: 200 }}
        value={value}
        title={title ?? 'subagent 使用的模型；改后对新会话生效'}
        onChange={(e) => {
          if (e.target.value === CUSTOM_MODEL) {
            setCustomText('')
            setCustomMode(true)
          } else {
            onAssign(e.target.value)
          }
        }}
      >
        <option value="">继承主会话模型</option>
        {candidateIds.map((id) => (
          <option key={id} value={id}>
            {candidateLabel(id)}
          </option>
        ))}
        <option value={CUSTOM_MODEL}>自定义模型 ID…</option>
        {stale && <option value={value}>{value}（当前，不在候选清单）</option>}
      </select>
      {stale && (
        <span style={{ fontSize: 12, color: 'var(--danger)' }} title="该模型不在当前接口/路由的候选清单内，调用时可能失败">
          不在候选清单
        </span>
      )}
    </span>
  )
}

/**
 * Agents 管理（轻量版）：列出内置类型与全局/项目级 agent 定义，指派各自使用的模型，可新建/删除。
 * 多 agent 协同时，subagent 按 frontmatter 的 model 字段走路由（实测 2.1.215 生效）。
 * 内置类型（Explore/Plan/general-purpose）无文件定义、默认继承主会话模型：
 * 指派模型 = 生成同名覆盖文件（整体替换内置定义，含提示词），恢复内置 = 删除覆盖文件。
 */
export function AgentsPage() {
  const [agents, setAgents] = useState<AgentInfo[]>([])
  const [builtins, setBuiltins] = useState<BuiltinAgentInfo[]>([])
  const [projects, setProjects] = useState<string[]>([])
  const [project, setProject] = useState('')
  /** 模型候选：官方三别名，或注入态下的接口/路由聚合清单 */
  const [candidates, setCandidates] = useState<ModelInfo[]>([])
  const [mode, setMode] = useState('official')
  const [error, setError] = useState('')
  /** 「已保存」闪现标记：自定义 agent 用文件路径，内置类型用 builtin:名称 */
  const [savedKey, setSavedKey] = useState('')
  /** 新建弹窗：scope 决定写入全局还是项目级目录 */
  const [creating, setCreating] = useState<'global' | 'project' | null>(null)
  /** 删除确认：自定义 agent */
  const [deleting, setDeleting] = useState<AgentInfo | null>(null)
  /** 恢复确认：内置类型（删除其覆盖文件） */
  const [restoring, setRestoring] = useState<BuiltinAgentInfo | null>(null)

  const flashSaved = (key: string) => {
    setSavedKey(key)
    setTimeout(() => setSavedKey(''), 1500)
  }

  const refresh = useCallback(async (proj: string) => {
    try {
      const [list, bl] = await Promise.all([
        agentsList(proj || undefined),
        agentsBuiltinList(proj || undefined)
      ])
      setAgents(list)
      setBuiltins(bl)
    } catch (e) {
      setError(String(e))
    }
  }, [])

  useEffect(() => {
    void (async () => {
      try {
        const cfg = await getConfig()
        const paths = cfg.projects.map((p) => p.path)
        setProjects(paths)
        setProject(paths[0] ?? '')
        const s = await apiInjectStatus()
        setMode(s.mode)
        if (s.mode !== 'official') {
          const list = await apiInjectActiveModels()
          setCandidates(list)
        }
        await refresh(paths[0] ?? '')
      } catch (e) {
        setError(String(e))
      }
    })()
  }, [refresh])

  const switchProject = (p: string) => {
    setProject(p)
    void refresh(p)
  }

  const assign = async (agent: AgentInfo, model: string) => {
    setError('')
    try {
      await agentsSetModel(agent.path, model)
      setAgents((all) => all.map((a) => (a.path === agent.path ? { ...a, model } : a)))
      flashSaved(agent.path)
    } catch (e) {
      setError(String(e))
    }
  }

  const assignBuiltin = async (b: BuiltinAgentInfo, model: string) => {
    setError('')
    try {
      // 首次覆盖写到当前选中项目（未选项目则全局）；已有覆盖时后端直接改原文件
      await agentsSetBuiltinModel({
        scope: project ? 'project' : 'global',
        projectPath: project || undefined,
        name: b.name,
        model
      })
      flashSaved(`builtin:${b.name}`)
      await refresh(project)
    } catch (e) {
      setError(String(e))
    }
  }

  const doDelete = async () => {
    if (!deleting) return
    setError('')
    try {
      await agentsDelete(deleting.path)
      setDeleting(null)
      await refresh(project)
    } catch (e) {
      setError(String(e))
      setDeleting(null)
    }
  }

  const doRestore = async () => {
    if (!restoring) return
    setError('')
    try {
      await agentsDelete(restoring.overridePath)
      setRestoring(null)
      await refresh(project)
    } catch (e) {
      setError(String(e))
      setRestoring(null)
    }
  }

  // 内置类型的覆盖文件会被 agentsList 扫到，从内建区统一管理，自定义区不再重复展示
  const builtinNames = new Set(builtins.map((b) => b.name))
  const globalAgents = agents.filter((a) => a.scope === 'global' && !builtinNames.has(a.name))
  const projectAgents = agents.filter((a) => a.scope === 'project' && !builtinNames.has(a.name))
  const globalNames = new Set(globalAgents.map((a) => a.name))
  const projectNames = new Set(projectAgents.map((a) => a.name))

  const candidateIds = mode === 'official' ? OFFICIAL_CANDIDATES : candidates.map((c) => c.id)
  const candidateLabel = (id: string) => {
    if (mode === 'official') return id
    return candidates.find((c) => c.id === id)?.displayName ?? id
  }

  const renderBuiltin = (b: BuiltinAgentInfo) => (
    <div key={b.name} className="row">
      <div className="row-main">
        <div className="row-title">
          {b.name}
          {b.overridePath ? (
            <span className="badge" style={{ marginLeft: 6 }} title={b.overridePath}>
              已覆盖（{b.overrideScope === 'project' ? '项目级' : '全局'}）
            </span>
          ) : (
            <span className="badge" style={{ marginLeft: 6, opacity: 0.6 }}>
              内置
            </span>
          )}
          {savedKey === `builtin:${b.name}` && (
            <span className="badge accent" style={{ marginLeft: 6 }}>
              已保存
            </span>
          )}
        </div>
        <div className="row-sub">
          {b.description}
          {b.overridePath && (
            <span style={{ marginLeft: 8, color: 'var(--text-faint)', fontFamily: 'var(--font-mono)' }}>
              {b.overridePath}
            </span>
          )}
        </div>
      </div>
      <div className="row-actions" style={{ alignItems: 'center', gap: 6 }}>
        <ModelSelect
          value={b.model}
          candidateIds={candidateIds}
          candidateLabel={candidateLabel}
          onAssign={(m) => void assignBuiltin(b, m)}
          title="为该内置类型指派模型；首次指派会生成同名覆盖文件（自定义定义整体替换内置 agent）"
        />
        {b.overridePath && (
          <>
            <button
              className="btn small"
              title="打开文件位置"
              onClick={() => void revealInExplorer(b.overridePath)}
            >
              打开
            </button>
            <button
              className="btn small danger"
              title="删除覆盖文件，恢复官方内置定义"
              onClick={() => setRestoring(b)}
            >
              恢复内置
            </button>
          </>
        )}
      </div>
    </div>
  )

  const renderRow = (a: AgentInfo) => (
    <div key={a.path} className="row">
      <div className="row-main">
        <div className="row-title">
          {a.name}
          {a.scope === 'project' && globalNames.has(a.name) && (
            <span
              className="badge"
              style={{ marginLeft: 6 }}
              title="与全局 agent 同名，会话内生效的是本项目级定义"
            >
              遮蔽全局同名
            </span>
          )}
          {a.scope === 'global' && projectNames.has(a.name) && (
            <span
              className="badge"
              style={{ marginLeft: 6, opacity: 0.6 }}
              title="项目级存在同名 agent，会话内实际生效的是项目级定义"
            >
              被项目级遮蔽
            </span>
          )}
          {savedKey === a.path && (
            <span className="badge accent" style={{ marginLeft: 6 }}>
              已保存
            </span>
          )}
        </div>
        <div className="row-sub">
          {a.description || '（无描述）'}
          <span style={{ marginLeft: 8, color: 'var(--text-faint)', fontFamily: 'var(--font-mono)' }}>
            {a.path}
          </span>
        </div>
      </div>
      <div className="row-actions" style={{ alignItems: 'center', gap: 6 }}>
        <ModelSelect
          value={a.model}
          candidateIds={candidateIds}
          candidateLabel={candidateLabel}
          onAssign={(m) => void assign(a, m)}
        />
        <button className="btn small" title="打开文件位置" onClick={() => void revealInExplorer(a.path)}>
          打开
        </button>
        <button className="btn small danger" title="删除该 agent 定义文件" onClick={() => setDeleting(a)}>
          删除
        </button>
      </div>
    </div>
  )

  return (
    <div className="page">
      <h2>Agents</h2>
      <p className="page-desc">
        为每个 agent 指派模型：多 agent 协同时 subagent 按此模型经多模型路由到对应接口执行。可直接在此新建
        agent，或在对话中让 Claude 生成（如「创建一个 code-reviewer subagent」）。
      </p>
      {error && <div style={{ color: 'var(--danger)', marginBottom: 10 }}>{error}</div>}

      <div style={{ marginBottom: 14 }}>
        <span style={{ fontSize: 13, color: 'var(--text-dim)', marginRight: 8 }}>
          模型候选：{mode === 'official' ? '官方别名（当前为官方模式）' : '当前接口/路由清单'}
        </span>
        {projects.length > 0 && (
          <span style={{ fontSize: 13, color: 'var(--text-dim)' }}>
            项目级：
            <select
              className="select"
              style={{ marginLeft: 6, minWidth: 180 }}
              value={project}
              onChange={(e) => switchProject(e.target.value)}
            >
              {projects.map((p) => (
                <option key={p} value={p}>
                  {p}
                </option>
              ))}
            </select>
          </span>
        )}
      </div>

      <div className="toolbar" style={{ marginBottom: 8 }}>
        <div style={{ fontWeight: 600 }}>内置类型（无文件定义，默认继承主会话模型）</div>
      </div>
      <div className="row-list" style={{ marginBottom: 4 }}>
        {builtins.map(renderBuiltin)}
      </div>
      <p style={{ fontSize: 12, color: 'var(--text-faint)', marginTop: 0, marginBottom: 18 }}>
        首次指派模型会在当前项目（未选项目则全局）生成同名覆盖文件：自定义定义整体替换内置
        agent（提示词为近似模板，可再编辑）；「恢复内置」删除覆盖文件，还原官方定义。
      </p>

      <div className="toolbar" style={{ marginBottom: 8 }}>
        <div style={{ fontWeight: 600 }}>全局（~/.claude/agents）</div>
        <div className="spacer" />
        <button className="btn small" onClick={() => setCreating('global')}>
          <IconPlus />
          新建
        </button>
      </div>
      <div className="row-list" style={{ marginBottom: 18 }}>
        {globalAgents.length === 0 && <div className="empty-hint">暂无全局 agent</div>}
        {globalAgents.map(renderRow)}
      </div>

      <div className="toolbar" style={{ marginBottom: 8 }}>
        <div style={{ fontWeight: 600 }}>项目级（.claude/agents）</div>
        <div className="spacer" />
        <button
          className="btn small"
          disabled={!project}
          title={project ? undefined : '请先添加项目'}
          onClick={() => setCreating('project')}
        >
          <IconPlus />
          新建
        </button>
      </div>
      <div className="row-list">
        {projectAgents.length === 0 && <div className="empty-hint">该项目暂无项目级 agent</div>}
        {projectAgents.map(renderRow)}
      </div>

      {creating && (
        <CreateDialog
          scope={creating}
          projectPath={project}
          candidateIds={candidateIds}
          candidateLabel={candidateLabel}
          onClose={() => setCreating(null)}
          onCreated={() => {
            setCreating(null)
            void refresh(project)
          }}
          onError={setError}
        />
      )}

      {deleting && (
        <ConfirmDialog
          title="删除 agent"
          message={`确定删除 agent「${deleting.name}」？\n定义文件将被移除：${deleting.path}`}
          confirmText="删除"
          danger
          onConfirm={() => void doDelete()}
          onCancel={() => setDeleting(null)}
        />
      )}

      {restoring && (
        <ConfirmDialog
          title="恢复内置"
          message={`确定恢复内置 agent「${restoring.name}」？\n覆盖文件将被删除，还原官方内置定义（含原提示词）：\n${restoring.overridePath}`}
          confirmText="恢复内置"
          danger
          onConfirm={() => void doRestore()}
          onCancel={() => setRestoring(null)}
        />
      )}
    </div>
  )
}

interface CreateDialogProps {
  scope: 'global' | 'project'
  projectPath: string
  candidateIds: string[]
  candidateLabel: (id: string) => string
  onClose: () => void
  onCreated: () => void
  onError: (msg: string) => void
}

/** 新建 agent 弹窗：名称/描述/模型/系统提示词（生成标准 frontmatter 的 .md 文件） */
function CreateDialog({
  scope,
  projectPath,
  candidateIds,
  candidateLabel,
  onClose,
  onCreated,
  onError
}: CreateDialogProps) {
  const [name, setName] = useState('')
  const [description, setDescription] = useState('')
  const [model, setModel] = useState('')
  const [prompt, setPrompt] = useState('')
  const [error, setError] = useState('')
  const [saving, setSaving] = useState(false)

  const canSave = name.trim() && description.trim() && !saving

  const save = async () => {
    setSaving(true)
    setError('')
    try {
      await agentsCreate({
        scope,
        projectPath: scope === 'project' ? projectPath : undefined,
        name: name.trim(),
        description: description.trim(),
        model,
        prompt
      })
      onCreated()
    } catch (e) {
      setError(String(e))
      setSaving(false)
      onError('')
    }
  }

  return (
    <Modal
      title={scope === 'global' ? '新建全局 agent' : '新建项目级 agent'}
      width={520}
      onClose={onClose}
      footer={
        <>
          <button className="btn" onClick={onClose}>
            取消
          </button>
          <button className="btn primary" disabled={!canSave} onClick={() => void save()}>
            {saving ? '创建中…' : '创建'}
          </button>
        </>
      }
    >
      {error && <div style={{ color: 'var(--danger)', marginBottom: 8 }}>{error}</div>}
      <label className="label">名称（subagent_type 引用名，kebab-case）</label>
      <input
        className="input"
        style={{ width: '100%', fontFamily: 'var(--font-mono)' }}
        placeholder="例如 code-reviewer"
        value={name}
        autoFocus
        onChange={(e) => setName(e.target.value)}
      />
      <label className="label">描述（主 agent 据此决定何时调用它）</label>
      <input
        className="input"
        style={{ width: '100%' }}
        placeholder="例如：审查代码改动，给出问题清单与修改建议"
        value={description}
        onChange={(e) => setDescription(e.target.value)}
      />
      <label className="label">模型（留空则继承主会话）</label>
      <ModelSelect
        value={model}
        candidateIds={candidateIds}
        candidateLabel={candidateLabel}
        onAssign={setModel}
      />
      <label className="label">系统提示词（可留空，生成后随时到文件里完善）</label>
      <textarea
        className="input"
        style={{ width: '100%', minHeight: 110, resize: 'vertical', fontFamily: 'inherit' }}
        placeholder="留空则生成最小可用模板"
        value={prompt}
        onChange={(e) => setPrompt(e.target.value)}
      />
    </Modal>
  )
}
