/**
 * Connections — 「工具与连接」页（MCP server 注册中心，P1）。
 *
 * 老师在这里接入外部 MCP 工具（如 arXiv 检索）。docs/MCP_SKILL_DESIGN.md：
 * server 卡片（状态点）、添加表单（transport 三选一）、测试连接（拉工具
 * 清单+风险预分级）、启停。stdio 可执行走 allowlist；凭证只引用环境变量名。
 */
import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import {
  Plug, Plus, Trash2, RefreshCw, X, CheckCircle2, XCircle,
  Loader2, AlertTriangle, Eye,
} from 'lucide-react'
import toast from 'react-hot-toast'
import { mcpApi, type McpServer, type ProbeResult } from '../api/mcp'
import { skillsApi, type PromptSkill } from '../api/skills'
import { PageHeader, Card, Button, Spinner, Badge } from '../components/ui'
import { clsx } from '../lib/clsx'

function riskBadge(risk: string) {
  if (risk === 'high') return <Badge variant="red">高危·需确认</Badge>
  if (risk === 'medium') return <Badge variant="yellow">中</Badge>
  return <Badge variant="default">低</Badge>
}

function statusDot(status?: string) {
  const map: Record<string, string> = {
    ok: 'bg-green-500', registered: 'bg-blue-400',
    unreachable: 'bg-red-500', auth_failed: 'bg-amber-500',
  }
  return <span className={clsx('w-2 h-2 rounded-full inline-block', map[status ?? ''] ?? 'bg-gray-300')} />
}

export default function Connections() {
  const qc = useQueryClient()
  const [section, setSection] = useState<'mcp' | 'skills'>('mcp')
  const [showAdd, setShowAdd] = useState(false)
  const [probes, setProbes] = useState<Record<string, ProbeResult>>({})
  const [probing, setProbing] = useState<string | null>(null)

  const { data, isLoading, isError } = useQuery({
    queryKey: ['mcp'], queryFn: mcpApi.list, retry: false,
  })

  const toggleMut = useMutation({
    mutationFn: ({ id, enabled }: { id: string; enabled: boolean }) =>
      mcpApi.update(id, { enabled }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['mcp'] }),
    onError: (e: any) => toast.error(e.message ?? '操作失败'),
  })
  const removeMut = useMutation({
    mutationFn: (id: string) => mcpApi.remove(id),
    onSuccess: () => { toast.success('已移除'); qc.invalidateQueries({ queryKey: ['mcp'] }) },
    onError: (e: any) => toast.error(e.message ?? '移除失败'),
  })

  async function probe(id: string) {
    setProbing(id)
    try {
      const r = await mcpApi.probe(id)
      setProbes(p => ({ ...p, [id]: r }))
      if (r.status === 'ok') toast.success(`连接成功，发现 ${r.tools.length} 个工具`)
      else if (r.status === 'registered') toast('已登记，stdio 将在运行时验证')
      else toast.error(`连接失败：${r.error || r.status}`)
    } catch (e: any) {
      toast.error(e.message ?? '测试失败')
    } finally { setProbing(null) }
  }

  const servers = data?.servers ?? []

  return (
    <div className="p-8 max-w-4xl mx-auto">
      <PageHeader
        title="工具与连接"
        description="接入外部 MCP 工具服务与能力插件，供智能体在对话中使用"
        action={section === 'mcp'
          ? <Button size="sm" icon={<Plus size={14} />} onClick={() => setShowAdd(true)}>添加 MCP</Button>
          : undefined}
      />

      <div className="flex gap-1 mb-5 border-b border-gray-200">
        {([['mcp', 'MCP 工具'], ['skills', '能力插件']] as const).map(([k, label]) => (
          <button key={k} onClick={() => setSection(k)}
            className={clsx('px-4 py-2 text-sm font-medium -mb-px border-b-2 transition-colors',
              section === k ? 'border-indigo-500 text-indigo-600' : 'border-transparent text-gray-500 hover:text-gray-700')}>
            {label}
          </button>
        ))}
      </div>

      {section === 'skills' && <SkillsSection />}

      {section === 'mcp' && <>
      <div className="mb-5 flex items-start gap-3 bg-amber-50 border border-amber-200 rounded-lg px-4 py-3 text-xs text-amber-800">
        <AlertTriangle size={15} className="mt-0.5 shrink-0 text-amber-600" />
        <div>
          只添加来自可信来源的 MCP server。stdio 类需要本机命令（限 npx/node/python 等已知运行器）；
          凭证请填环境变量名（写在 <code>~/.agentpaas/.env</code>），不直接存密钥。
          MCP 的删除/发送类工具默认需人工确认。
        </div>
      </div>

      {isLoading ? <div className="flex justify-center py-16"><Spinner size={32} /></div>
        : isError ? (
          <div className="rounded-xl border border-yellow-200 bg-yellow-50 p-6 text-sm text-yellow-800">
            无法连接后端服务，请确认 AgentPaaS 正在运行。
          </div>
        ) : servers.length === 0 ? (
          <Card className="text-center py-12">
            <Plug size={36} className="mx-auto text-gray-300 mb-3" />
            <p className="text-sm text-gray-500 mb-4">还没有接入任何 MCP 工具</p>
            <Button size="sm" icon={<Plus size={14} />} onClick={() => setShowAdd(true)}>添加第一个</Button>
          </Card>
        ) : (
          <div className="space-y-3">
            {servers.map(s => {
              const pr = probes[s.id]
              return (
                <Card key={s.id} className="p-0 overflow-hidden">
                  <div className="flex items-center gap-3 px-4 py-3">
                    {statusDot(pr?.status)}
                    <div className="min-w-0 flex-1">
                      <p className="text-sm font-medium text-gray-900 truncate">
                        {s.name || s.id} <span className="text-[10px] text-gray-400 font-mono">{s.transport}</span>
                      </p>
                      <p className="text-xs text-gray-400 truncate font-mono">
                        {s.transport === 'stdio' ? s.command_argv.join(' ') : s.url}
                        {s.auth?.kind !== 'none' && s.auth?.env_key && `  · 凭证 $${s.auth.env_key}`}
                      </p>
                    </div>
                    {!s.enabled && <Badge variant="default">已禁用</Badge>}
                    <Button size="sm" variant="secondary" disabled={probing === s.id}
                      icon={probing === s.id ? <Loader2 size={13} className="animate-spin" /> : <RefreshCw size={13} />}
                      onClick={() => probe(s.id)}>测试</Button>
                    <button
                      onClick={() => toggleMut.mutate({ id: s.id, enabled: !s.enabled })}
                      className={clsx('text-xs px-2.5 py-1.5 rounded-lg transition-colors',
                        s.enabled ? 'bg-green-50 text-green-700 hover:bg-green-100'
                          : 'bg-gray-100 text-gray-500 hover:bg-gray-200')}
                    >{s.enabled ? '启用中' : '已停用'}</button>
                    <button onClick={() => { if (confirm(`移除 ${s.name || s.id}？`)) removeMut.mutate(s.id) }}
                      className="text-gray-300 hover:text-red-500 p-1"><Trash2 size={14} /></button>
                  </div>
                  {pr && pr.tools.length > 0 && (
                    <div className="border-t border-gray-100 px-4 py-2.5 bg-gray-50/50">
                      <p className="text-[11px] text-gray-400 mb-1.5">{pr.tools.length} 个工具：</p>
                      <div className="space-y-1">
                        {pr.tools.map(t => (
                          <div key={t.name} className="flex items-center gap-2 text-xs">
                            <span className="font-mono text-gray-700">{t.name}</span>
                            {riskBadge(t.risk)}
                            <span className="text-gray-400 truncate">{t.description}</span>
                          </div>
                        ))}
                      </div>
                    </div>
                  )}
                  {pr && pr.status !== 'ok' && (
                    <div className="border-t border-gray-100 px-4 py-2 text-xs flex items-center gap-1.5">
                      {pr.status === 'registered'
                        ? <><CheckCircle2 size={12} className="text-blue-400" /><span className="text-gray-500">已登记，stdio 在运行时验证</span></>
                        : <><XCircle size={12} className="text-red-400" /><span className="text-red-500">{pr.error || pr.status}</span></>}
                    </div>
                  )}
                </Card>
              )
            })}
          </div>
        )}

      {showAdd && <AddModal onClose={() => setShowAdd(false)}
        onAdded={() => { setShowAdd(false); qc.invalidateQueries({ queryKey: ['mcp'] }) }} />}
      </>}
    </div>
  )
}

function AddModal({ onClose, onAdded }: { onClose: () => void; onAdded: () => void }) {
  const [id, setId] = useState('')
  const [name, setName] = useState('')
  const [transport, setTransport] = useState('http')
  const [url, setUrl] = useState('')
  const [argv, setArgv] = useState('')
  const [envKey, setEnvKey] = useState('')

  const addMut = useMutation({
    mutationFn: () => mcpApi.add({
      id, name, transport,
      url: transport !== 'stdio' ? url : '',
      command_argv: transport === 'stdio' ? argv.trim().split(/\s+/).filter(Boolean) : [],
      auth: envKey ? { kind: 'env_bearer', env_key: envKey } : { kind: 'none' },
    }),
    onSuccess: () => { toast.success('已添加'); onAdded() },
    onError: (e: any) => toast.error(e.message ?? '添加失败'),
  })

  return (
    <div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50" onClick={onClose}>
      <div className="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 p-5" onClick={e => e.stopPropagation()}>
        <div className="flex items-center justify-between mb-4">
          <h3 className="text-base font-semibold text-gray-900">添加 MCP server</h3>
          <button onClick={onClose} className="text-gray-300 hover:text-gray-500"><X size={18} /></button>
        </div>
        <div className="space-y-3">
          <div className="grid grid-cols-2 gap-3">
            <label className="block">
              <span className="text-xs text-gray-500">ID（英文短名）</span>
              <input value={id} onChange={e => setId(e.target.value)} placeholder="arxiv-search"
                className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5" />
            </label>
            <label className="block">
              <span className="text-xs text-gray-500">显示名</span>
              <input value={name} onChange={e => setName(e.target.value)} placeholder="arXiv 检索"
                className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5" />
            </label>
          </div>
          <label className="block">
            <span className="text-xs text-gray-500">连接方式</span>
            <select value={transport} onChange={e => setTransport(e.target.value)}
              className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 bg-white">
              <option value="http">http（JSON-RPC 端点）</option>
              <option value="sse">sse（流式端点）</option>
              <option value="stdio">stdio（本机命令）</option>
            </select>
          </label>
          {transport === 'stdio' ? (
            <label className="block">
              <span className="text-xs text-gray-500">启动命令（限 npx/node/python 等）</span>
              <input value={argv} onChange={e => setArgv(e.target.value)} placeholder="npx arxiv-mcp"
                className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 font-mono" />
            </label>
          ) : (
            <label className="block">
              <span className="text-xs text-gray-500">端点 URL</span>
              <input value={url} onChange={e => setUrl(e.target.value)} placeholder="http://localhost:9000/mcp"
                className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 font-mono" />
            </label>
          )}
          <label className="block">
            <span className="text-xs text-gray-500">凭证环境变量名（可选，值写在 ~/.agentpaas/.env）</span>
            <input value={envKey} onChange={e => setEnvKey(e.target.value)} placeholder="ARXIV_API_KEY"
              className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 font-mono" />
          </label>
        </div>
        <div className="flex justify-end gap-2 mt-5">
          <Button size="sm" variant="secondary" onClick={onClose}>取消</Button>
          <Button size="sm" disabled={!id || addMut.isPending}
            onClick={() => addMut.mutate()}>{addMut.isPending ? '添加中…' : '添加'}</Button>
        </div>
      </div>
    </div>
  )
}


// ── 能力插件（prompt skill）──
function SkillsSection() {
  const qc = useQueryClient()
  const [adding, setAdding] = useState(false)
  const [viewing, setViewing] = useState<PromptSkill | null>(null)
  const { data } = useQuery({ queryKey: ['skills'], queryFn: skillsApi.list, retry: false })
  const toggleMut = useMutation({
    mutationFn: ({ name, enabled }: { name: string; enabled: boolean }) => skillsApi.update(name, { enabled }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['skills'] }),
  })
  const removeMut = useMutation({
    mutationFn: (name: string) => skillsApi.remove(name),
    onSuccess: () => { toast.success('已删除'); qc.invalidateQueries({ queryKey: ['skills'] }) },
  })
  const skills = data?.skills ?? []
  return (
    <>
      <div className="mb-4 flex items-start gap-3 bg-blue-50 border border-blue-200 rounded-lg px-4 py-3 text-xs text-blue-800">
        <AlertTriangle size={15} className="mt-0.5 shrink-0 text-blue-600" />
        <div>
          能力插件是一段提示词 + 工具依赖，挂载到智能体即扩充其能力（在智能体编辑页 <code>skills</code> 引用）。
          系统规则始终优先于插件提示词；高风险工具（Bash/写删类）不会被插件自动授予。
        </div>
      </div>
      <div className="flex justify-end mb-3">
        <Button size="sm" icon={<Plus size={14} />} onClick={() => setAdding(true)}>新建能力插件</Button>
      </div>
      {skills.length === 0 ? (
        <Card className="text-center py-12">
          <Plug size={36} className="mx-auto text-gray-300 mb-3" />
          <p className="text-sm text-gray-500">还没有能力插件</p>
        </Card>
      ) : (
        <div className="space-y-3">
          {skills.map(s => (
            <Card key={s.name} className="p-4">
              <div className="flex items-center gap-2">
                <span className="text-sm font-medium text-gray-900">{s.name}</span>
                <span className="text-[10px] text-gray-400 font-mono">v{s.version}</span>
                {!s.enabled && <Badge variant="default">已停用</Badge>}
                <div className="flex-1" />
                <button onClick={() => setViewing(s)}
                  className="text-xs px-2.5 py-1 rounded-lg bg-gray-100 text-gray-600 hover:bg-gray-200 inline-flex items-center gap-1">
                  <Eye size={13} /> 查看
                </button>
                <button onClick={() => toggleMut.mutate({ name: s.name, enabled: !s.enabled })}
                  className={clsx('text-xs px-2.5 py-1 rounded-lg',
                    s.enabled ? 'bg-green-50 text-green-700 hover:bg-green-100' : 'bg-gray-100 text-gray-500 hover:bg-gray-200')}>
                  {s.enabled ? '启用中' : '已停用'}
                </button>
                <button onClick={() => { if (confirm(`删除 ${s.name}？`)) removeMut.mutate(s.name) }}
                  className="text-gray-300 hover:text-red-500 p-1"><Trash2 size={14} /></button>
              </div>
              {s.description && <p className="text-xs text-gray-500 mt-1">{s.description}</p>}
              {s.requires?.tools?.length > 0 && (
                <div className="flex flex-wrap gap-1 mt-2">
                  {s.requires.tools.map(t => (
                    <span key={t} className="text-[10px] bg-gray-100 text-gray-600 px-1.5 py-0.5 rounded font-mono">{t}</span>
                  ))}
                </div>
              )}
            </Card>
          ))}
        </div>
      )}
      {adding && <AddSkillModal onClose={() => setAdding(false)}
        onAdded={() => { setAdding(false); qc.invalidateQueries({ queryKey: ['skills'] }) }} />}
      {viewing && <ViewSkillModal skill={viewing} onClose={() => setViewing(null)} />}
    </>
  )
}

// ── 查看能力插件详情（只读）──
function ViewSkillModal({ skill, onClose }: { skill: PromptSkill; onClose: () => void }) {
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4" onClick={onClose}>
      <div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[85vh] overflow-y-auto"
        onClick={e => e.stopPropagation()}>
        <div className="flex items-center gap-2 px-5 py-3.5 border-b border-gray-100 sticky top-0 bg-white">
          <h3 className="text-base font-semibold text-gray-900">{skill.name}</h3>
          <span className="text-[10px] text-gray-400 font-mono">v{skill.version}</span>
          {!skill.enabled && <Badge variant="default">已停用</Badge>}
          <div className="flex-1" />
          <button onClick={onClose} className="text-gray-400 hover:text-gray-600 p-1"><X size={16} /></button>
        </div>
        <div className="px-5 py-4 space-y-4">
          {skill.description && (
            <div>
              <div className="text-xs font-medium text-gray-500 mb-1">描述</div>
              <p className="text-sm text-gray-700">{skill.description}</p>
            </div>
          )}
          <div>
            <div className="text-xs font-medium text-gray-500 mb-1">提示词（挂载后追加到智能体）</div>
            <pre className="text-xs text-gray-800 bg-gray-50 border border-gray-200 rounded-lg p-3 whitespace-pre-wrap font-mono leading-relaxed">{skill.prompt}</pre>
          </div>
          <div>
            <div className="text-xs font-medium text-gray-500 mb-1.5">依赖工具</div>
            {skill.requires?.tools?.length > 0 ? (
              <div className="flex flex-wrap gap-1">
                {skill.requires.tools.map(t => (
                  <span key={t} className="text-[10px] bg-gray-100 text-gray-600 px-1.5 py-0.5 rounded font-mono">{t}</span>
                ))}
              </div>
            ) : <p className="text-xs text-gray-400">无</p>}
          </div>
          {skill.requires?.mcp_scopes?.length > 0 && (
            <div>
              <div className="text-xs font-medium text-gray-500 mb-1.5">MCP 权限范围</div>
              <div className="flex flex-wrap gap-1">
                {skill.requires.mcp_scopes.map(s => (
                  <span key={s} className="text-[10px] bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded font-mono">{s}</span>
                ))}
              </div>
            </div>
          )}
          {skill.examples?.length > 0 && (
            <div>
              <div className="text-xs font-medium text-gray-500 mb-1.5">示例</div>
              <ul className="space-y-1">
                {skill.examples.map((ex, i) => (
                  <li key={i} className="text-xs text-gray-600 bg-gray-50 border border-gray-100 rounded px-2 py-1">{ex}</li>
                ))}
              </ul>
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

function AddSkillModal({ onClose, onAdded }: { onClose: () => void; onAdded: () => void }) {
  const [name, setName] = useState('')
  const [desc, setDesc] = useState('')
  const [prompt, setPrompt] = useState('')
  const [tools, setTools] = useState('')
  const addMut = useMutation({
    mutationFn: () => skillsApi.add({
      name, description: desc, prompt,
      requires: { tools: tools.trim() ? tools.split(/[\s,]+/).filter(Boolean) : [], mcp_scopes: [] },
    }),
    onSuccess: () => { toast.success('已创建'); onAdded() },
    onError: (e: any) => toast.error(e.message ?? '创建失败'),
  })
  return (
    <div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50" onClick={onClose}>
      <div className="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 p-5" onClick={e => e.stopPropagation()}>
        <div className="flex items-center justify-between mb-4">
          <h3 className="text-base font-semibold text-gray-900">新建能力插件</h3>
          <button onClick={onClose} className="text-gray-300 hover:text-gray-500"><X size={18} /></button>
        </div>
        <div className="space-y-3">
          <label className="block">
            <span className="text-xs text-gray-500">名称（英文短名）</span>
            <input value={name} onChange={e => setName(e.target.value)} placeholder="pdf-export"
              className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 font-mono" />
          </label>
          <label className="block">
            <span className="text-xs text-gray-500">描述</span>
            <input value={desc} onChange={e => setDesc(e.target.value)} placeholder="把产物导出为 PDF"
              className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5" />
          </label>
          <label className="block">
            <span className="text-xs text-gray-500">提示词（挂载时追加到系统提示之后）</span>
            <textarea value={prompt} onChange={e => setPrompt(e.target.value)} rows={4}
              placeholder="需要导出 PDF 时调用 DocGen，中文用 PingFang SC 字体…"
              className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 resize-y" />
          </label>
          <label className="block">
            <span className="text-xs text-gray-500">依赖工具（空格分隔，须为内置工具名）</span>
            <input value={tools} onChange={e => setTools(e.target.value)} placeholder="DocGen"
              className="mt-1 w-full text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 font-mono" />
          </label>
        </div>
        <div className="flex justify-end gap-2 mt-5">
          <Button size="sm" variant="secondary" onClick={onClose}>取消</Button>
          <Button size="sm" disabled={!name || !prompt || addMut.isPending}
            onClick={() => addMut.mutate()}>{addMut.isPending ? '创建中…' : '创建'}</Button>
        </div>
      </div>
    </div>
  )
}
