import { useEffect, useMemo, useState } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import {
  AlertTriangle, Bot, CheckCircle2, ChevronRight, CircleDot, Database,
  FileCheck2, GitBranch, Layers3, Network, Play, Plus, RefreshCw, Search,
  ShieldCheck, Sparkles, Workflow, X,
} from 'lucide-react'
import toast from 'react-hot-toast'
import {
  governanceApi, type GovernanceAssertion, type GovernanceIssue,
  type GovernanceEvidence, type OntologyElement, type OntologyLayer, type OntologyProfile,
} from '../api/governance'
import { Badge, Button, Card, EmptyState, Input, PageHeader, Spinner, Tabs, Textarea } from '../components/ui'

const LAYERS: Array<{ id: OntologyLayer; label: string; description: string; tone: string }> = [
  { id: 'M3', label: 'M3 建模内核', description: '定义如何建立本体', tone: 'border-violet-200 bg-violet-50' },
  { id: 'M2', label: 'M2 元模型与 Profile', description: '定义业务、软件与治理语言', tone: 'border-blue-200 bg-blue-50' },
  { id: 'M1', label: 'M1 项目模型', description: '正式模型与智能体候选区', tone: 'border-amber-200 bg-amber-50' },
  { id: 'M0', label: 'M0 实例与事实', description: '真实资产、观测、测量和证据', tone: 'border-emerald-200 bg-emerald-50' },
]

const PROFILE_META: Record<OntologyProfile, { label: string; color: string }> = {
  meta: { label: '建模内核', color: 'bg-violet-100 text-violet-700' },
  upper: { label: '通用上层', color: 'bg-slate-100 text-slate-700' },
  business: { label: '业务 Profile', color: 'bg-amber-100 text-amber-700' },
  software: { label: '软件 Profile', color: 'bg-blue-100 text-blue-700' },
  governance: { label: '治理 Profile', color: 'bg-emerald-100 text-emerald-700' },
  cross: { label: '跨域关系', color: 'bg-fuchsia-100 text-fuchsia-700' },
}

const SAMPLE_DDL = `CREATE TABLE dcp_customer (
  id BIGINT PRIMARY KEY,
  tenant_id VARCHAR(64) NOT NULL,
  customer_name VARCHAR(128) NOT NULL,
  mobile VARCHAR(32),
  email VARCHAR(128),
  created_at TIMESTAMP NOT NULL
);`

function severityBadge(severity: string) {
  if (severity === 'critical') return <Badge variant="red">严重</Badge>
  if (severity === 'high') return <Badge variant="yellow">高</Badge>
  if (severity === 'medium') return <Badge variant="blue">中</Badge>
  return <Badge>低</Badge>
}

function reviewBadge(status: string) {
  if (status === 'accepted') return <Badge variant="green">已接受</Badge>
  if (status === 'rejected') return <Badge variant="red">已拒绝</Badge>
  if (status === 'expired') return <Badge>已过期</Badge>
  return <Badge variant="yellow">待审核</Badge>
}

function profileChip(profile: OntologyProfile) {
  const meta = PROFILE_META[profile] ?? PROFILE_META.upper
  return <span className={`inline-flex px-2 py-0.5 rounded-full text-[11px] font-medium ${meta.color}`}>{meta.label}</span>
}

export default function Governance() {
  const qc = useQueryClient()
  const [activeProjectId, setActiveProjectId] = useState('')
  const [tab, setTab] = useState('overview')
  const [layerFilter, setLayerFilter] = useState('')
  const [profileFilter, setProfileFilter] = useState('')
  const [query, setQuery] = useState('')
  const [selectedElementId, setSelectedElementId] = useState('')
  const [showProjectForm, setShowProjectForm] = useState(false)
  const [projectName, setProjectName] = useState('')
  const [projectNamespace, setProjectNamespace] = useState('https://onto.qm.cn/ontorefactor/model/')
  const [showAnalyze, setShowAnalyze] = useState(false)
  const [sourceType, setSourceType] = useState('ddl')
  const [sourceName, setSourceName] = useState('customer-schema.sql')
  const [environment, setEnvironment] = useState('development')
  const [sourceContent, setSourceContent] = useState(SAMPLE_DDL)
  const [lastAgentResult, setLastAgentResult] = useState<null | {
    run_id: string
    result: { summary: { elements: number; assertions: number; issues: number }; agents: string[] }
    trace: Array<{ term_name: string; duration_ms: number }>
  }>(null)

  const projectsQ = useQuery({
    queryKey: ['governance', 'projects'], queryFn: governanceApi.projects, retry: false,
  })
  const projects = projectsQ.data?.projects ?? []

  useEffect(() => {
    if (!activeProjectId && projects.length > 0) setActiveProjectId(projects[0].id)
    if (activeProjectId && projects.length > 0 && !projects.some(p => p.id === activeProjectId)) {
      setActiveProjectId(projects[0].id)
    }
  }, [projects, activeProjectId])

  const refresh = () => qc.invalidateQueries({ queryKey: ['governance'] })
  const enabled = Boolean(activeProjectId)
  const overviewQ = useQuery({
    queryKey: ['governance', activeProjectId, 'overview'],
    queryFn: () => governanceApi.overview(activeProjectId), enabled,
  })
  const elementsQ = useQuery({
    queryKey: ['governance', activeProjectId, 'elements', layerFilter, profileFilter, query],
    queryFn: () => governanceApi.elements(activeProjectId, { layer: layerFilter, profile: profileFilter, q: query }),
    enabled,
  })
  const graphQ = useQuery({
    queryKey: ['governance', activeProjectId, 'graph'],
    queryFn: () => governanceApi.graph(activeProjectId), enabled,
  })
  const assertionsQ = useQuery({
    queryKey: ['governance', activeProjectId, 'assertions'],
    queryFn: () => governanceApi.assertions(activeProjectId), enabled,
  })
  const evidenceQ = useQuery({
    queryKey: ['governance', activeProjectId, 'evidence'],
    queryFn: () => governanceApi.evidence(activeProjectId), enabled,
  })
  const issuesQ = useQuery({
    queryKey: ['governance', activeProjectId, 'issues'],
    queryFn: () => governanceApi.issues(activeProjectId), enabled,
  })
  const rulesQ = useQuery({
    queryKey: ['governance', activeProjectId, 'rules'],
    queryFn: () => governanceApi.rules(activeProjectId), enabled,
  })
  const runsQ = useQuery({
    queryKey: ['governance', activeProjectId, 'agent-runs'],
    queryFn: () => governanceApi.agentRuns(activeProjectId), enabled,
  })
  const impactQ = useQuery({
    queryKey: ['governance', selectedElementId, 'impact'],
    queryFn: () => governanceApi.impact(selectedElementId), enabled: Boolean(selectedElementId),
  })

  const bootstrap = useMutation({
    mutationFn: governanceApi.bootstrap,
    onSuccess: data => {
      setActiveProjectId(data.project.id)
      refresh()
      toast.success('已加载 M3-M0 租户隔离纵向样例')
    },
    onError: (error: Error) => toast.error(error.message),
  })
  const createProject = useMutation({
    mutationFn: () => governanceApi.createProject({
      name: projectName.trim(), description: 'OntoRefactor 四层本体项目',
      namespace: projectNamespace.trim(),
    }),
    onSuccess: project => {
      setShowProjectForm(false)
      setActiveProjectId(project.id)
      setProjectName('')
      refresh()
      toast.success('本体项目已创建')
    },
    onError: (error: Error) => toast.error(error.message),
  })
  const validate = useMutation({
    mutationFn: () => governanceApi.validate(activeProjectId),
    onSuccess: result => {
      refresh()
      result.valid ? toast.success('模型通过全部高优先级门禁') : toast.error(`发现 ${result.findings.length} 个治理问题`)
    },
    onError: (error: Error) => toast.error(error.message),
  })
  const analyze = useMutation({
    mutationFn: () => governanceApi.analyze(activeProjectId, {
      source_type: sourceType, content: sourceContent, source_name: sourceName,
      environment, persist: true,
    }),
    onSuccess: result => {
      setLastAgentResult(result)
      refresh()
      toast.success(`智能体已生成 ${result.result.summary.elements} 个元素候选`)
    },
    onError: (error: Error) => toast.error(error.message),
  })
  const review = useMutation({
    mutationFn: ({ assertion, status }: { assertion: GovernanceAssertion; status: 'accepted' | 'rejected' }) =>
      governanceApi.reviewAssertion(assertion.id, status, 'ontology-expert', 'Web 工作台人工审核'),
    onSuccess: () => { refresh(); toast.success('断言审核状态已更新') },
    onError: (error: Error) => toast.error(error.message),
  })
  const updateIssue = useMutation({
    mutationFn: ({ issue, status }: { issue: GovernanceIssue; status: GovernanceIssue['status'] }) =>
      governanceApi.updateIssue(issue.id, status, issue.assigned_to),
    onSuccess: () => { refresh(); toast.success('问题状态已更新') },
    onError: (error: Error) => toast.error(error.message),
  })

  const elementsByLayer = useMemo(() => {
    const grouped: Record<OntologyLayer, OntologyElement[]> = { M3: [], M2: [], M1: [], M0: [] }
    for (const element of elementsQ.data?.items ?? []) grouped[element.layer].push(element)
    return grouped
  }, [elementsQ.data])

  if (projectsQ.isLoading) return <div className="p-12 flex justify-center"><Spinner size={28} /></div>

  return (
    <div className="p-8 max-w-7xl mx-auto">
      <PageHeader
        title="数据治理与本体"
        description="以 M3-M0 四层元建模统一业务、软件、治理 Profile，用证据和人工审核约束智能体结论。"
        action={(
          <div className="flex gap-2">
            <Button variant="secondary" icon={<Plus size={14} />} onClick={() => setShowProjectForm(true)}>新建项目</Button>
            <Button icon={<Sparkles size={14} />} loading={bootstrap.isPending} onClick={() => bootstrap.mutate()}>加载纵向样例</Button>
          </div>
        )}
      />

      {projects.length === 0 ? (
        <Card>
          <EmptyState
            icon={<Network size={46} />}
            title="还没有本体项目"
            description="建议先加载“租户隔离”纵向样例，它包含 M3 内核、M2 三个 Profile、M1 DCP 模型和 M0 运行事实。"
            action={<Button loading={bootstrap.isPending} onClick={() => bootstrap.mutate()}>加载 M3-M0 示例</Button>}
          />
        </Card>
      ) : (
        <>
          <div className="mb-6 flex items-center gap-3 bg-white border border-gray-200 rounded-xl px-4 py-3">
            <Layers3 size={18} className="text-indigo-500" />
            <select
              value={activeProjectId}
              onChange={event => setActiveProjectId(event.target.value)}
              className="min-w-64 text-sm font-medium text-gray-900 bg-transparent focus:outline-none"
            >
              {projects.map(project => <option key={project.id} value={project.id}>{project.name} · v{project.version}</option>)}
            </select>
            <span className="text-xs text-gray-400 truncate flex-1">{overviewQ.data?.project.namespace}</span>
            <Button variant="secondary" size="sm" icon={<RefreshCw size={13} />} loading={validate.isPending} onClick={() => validate.mutate()}>
              执行治理门禁
            </Button>
            <Button size="sm" icon={<Bot size={13} />} onClick={() => { setTab('agents'); setShowAnalyze(true) }}>
              智能抽取
            </Button>
          </div>

          <Tabs
            tabs={[
              { id: 'overview', label: '治理总览' },
              { id: 'model', label: '四层模型' },
              { id: 'assertions', label: `断言审核${overviewQ.data?.totals.pending_reviews ? ` (${overviewQ.data.totals.pending_reviews})` : ''}` },
              { id: 'evidence', label: `证据库${overviewQ.data?.totals.evidence ? ` (${overviewQ.data.totals.evidence})` : ''}` },
              { id: 'issues', label: `问题与门禁${overviewQ.data?.totals.open_issues ? ` (${overviewQ.data.totals.open_issues})` : ''}` },
              { id: 'agents', label: '智能体运行' },
            ]}
            active={tab}
            onChange={setTab}
          />

          {tab === 'overview' && <OverviewTab overview={overviewQ.data} rules={rulesQ.data?.rules ?? []} edges={graphQ.data?.edges ?? []} />}
          {tab === 'model' && (
            <ModelTab
              elementsByLayer={elementsByLayer}
              loading={elementsQ.isLoading}
              layerFilter={layerFilter}
              profileFilter={profileFilter}
              query={query}
              setLayerFilter={setLayerFilter}
              setProfileFilter={setProfileFilter}
              setQuery={setQuery}
              onSelect={setSelectedElementId}
              edges={graphQ.data?.edges ?? []}
            />
          )}
          {tab === 'assertions' && (
            <AssertionsTab
              assertions={assertionsQ.data?.assertions ?? []}
              loading={assertionsQ.isLoading}
              onReview={(assertion, status) => review.mutate({ assertion, status })}
              reviewing={review.isPending}
            />
          )}
          {tab === 'evidence' && (
            <EvidenceTab evidence={evidenceQ.data?.evidence ?? []} loading={evidenceQ.isLoading} />
          )}
          {tab === 'issues' && (
            <IssuesTab
              issues={issuesQ.data?.issues ?? []}
              loading={issuesQ.isLoading}
              onUpdate={(issue, status) => updateIssue.mutate({ issue, status })}
            />
          )}
          {tab === 'agents' && (
            <AgentsTab
              showAnalyze={showAnalyze}
              setShowAnalyze={setShowAnalyze}
              sourceType={sourceType}
              setSourceType={setSourceType}
              sourceName={sourceName}
              setSourceName={setSourceName}
              environment={environment}
              setEnvironment={setEnvironment}
              content={sourceContent}
              setContent={setSourceContent}
              onAnalyze={() => analyze.mutate()}
              analyzing={analyze.isPending}
              lastResult={lastAgentResult}
              runs={runsQ.data?.runs ?? []}
            />
          )}
        </>
      )}

      {showProjectForm && (
        <Modal title="新建四层本体项目" onClose={() => setShowProjectForm(false)}>
          <div className="space-y-4">
            <Input label="项目名称" value={projectName} onChange={event => setProjectName(event.target.value)} placeholder="例如：DCP 身份认证域" />
            <Input label="命名空间" value={projectNamespace} onChange={event => setProjectNamespace(event.target.value)} />
            <div className="flex justify-end gap-2">
              <Button variant="secondary" onClick={() => setShowProjectForm(false)}>取消</Button>
              <Button disabled={!projectName.trim() || !projectNamespace.trim()} loading={createProject.isPending} onClick={() => createProject.mutate()}>创建</Button>
            </div>
          </div>
        </Modal>
      )}

      {selectedElementId && (
        <Modal title="影响路径" onClose={() => setSelectedElementId('')} wide>
          {impactQ.isLoading ? <div className="py-10 flex justify-center"><Spinner /></div> : (
            <div>
              <div className="flex items-center gap-2 mb-4">
                <Network size={18} className="text-indigo-500" />
                <span className="font-semibold">{impactQ.data?.center.name}</span>
                {impactQ.data?.center && profileChip(impactQ.data.center.profile)}
              </div>
              <div className="space-y-2 max-h-[55vh] overflow-y-auto">
                {(impactQ.data?.nodes ?? []).map(node => (
                  <div key={node.id} className="flex items-center gap-3 border border-gray-100 rounded-lg px-3 py-2">
                    <span className="w-8 h-8 rounded-lg bg-gray-100 flex items-center justify-center text-xs font-bold text-gray-600">{node.layer}</span>
                    <div className="min-w-0 flex-1">
                      <p className="text-sm font-medium truncate">{node.name}</p>
                      <p className="text-xs text-gray-400">{node.kind} · 距离 {node.distance}</p>
                    </div>
                    {profileChip(node.profile)}
                  </div>
                ))}
              </div>
              <p className="mt-4 text-xs text-gray-500">共命中 {impactQ.data?.nodes.length ?? 0} 个节点、{impactQ.data?.edges.length ?? 0} 条已接受断言。</p>
            </div>
          )}
        </Modal>
      )}
    </div>
  )
}

function OverviewTab({ overview, rules, edges }: {
  overview?: Awaited<ReturnType<typeof governanceApi.overview>>
  rules: Array<{ id: string; name: string; severity: string; description: string; enabled: boolean }>
  edges: GovernanceAssertion[]
}) {
  if (!overview) return <div className="py-12 flex justify-center"><Spinner /></div>
  const totals = overview.totals
  return (
    <div className="space-y-6">
      <div className="grid grid-cols-2 lg:grid-cols-5 gap-3">
        {[
          { label: '本体元素', value: totals.elements, icon: <Database size={18} />, color: 'text-blue-500' },
          { label: '统一断言', value: totals.assertions, icon: <GitBranch size={18} />, color: 'text-violet-500' },
          { label: '证据', value: totals.evidence, icon: <FileCheck2 size={18} />, color: 'text-emerald-500' },
          { label: '待审核', value: totals.pending_reviews, icon: <CircleDot size={18} />, color: 'text-amber-500' },
          { label: '开放问题', value: totals.open_issues, icon: <AlertTriangle size={18} />, color: totals.open_issues ? 'text-red-500' : 'text-gray-400' },
        ].map(item => (
          <Card key={item.label} className="p-4">
            <div className="flex items-center justify-between"><span className="text-xs text-gray-500">{item.label}</span><span className={item.color}>{item.icon}</span></div>
            <p className="text-2xl font-bold mt-2">{item.value}</p>
          </Card>
        ))}
      </div>

      <div className="grid lg:grid-cols-2 gap-5">
        <Card>
          <h3 className="text-sm font-semibold mb-4 flex items-center gap-2"><Layers3 size={16} className="text-indigo-500" />四层语义覆盖</h3>
          <div className="space-y-3">
            {LAYERS.map(layer => {
              const count = overview.layers[layer.id] ?? 0
              const width = Math.max(4, Math.round((count / Math.max(totals.elements, 1)) * 100))
              return (
                <div key={layer.id}>
                  <div className="flex justify-between text-xs mb-1"><span className="font-medium">{layer.label}</span><span className="text-gray-400">{count}</span></div>
                  <div className="h-2 bg-gray-100 rounded-full overflow-hidden"><div className="h-full bg-indigo-500 rounded-full" style={{ width: `${width}%` }} /></div>
                </div>
              )
            })}
          </div>
        </Card>
        <Card>
          <h3 className="text-sm font-semibold mb-4 flex items-center gap-2"><Workflow size={16} className="text-emerald-500" />Profile 覆盖</h3>
          <div className="grid grid-cols-2 gap-3">
            {(Object.keys(PROFILE_META) as OntologyProfile[]).map(profile => (
              <div key={profile} className="border border-gray-100 rounded-lg p-3">
                {profileChip(profile)}
                <p className="text-xl font-semibold mt-2">{overview.profiles[profile] ?? 0}</p>
              </div>
            ))}
          </div>
        </Card>
      </div>

      <div className="grid lg:grid-cols-2 gap-5">
        <Card>
          <h3 className="text-sm font-semibold mb-3 flex items-center gap-2"><ShieldCheck size={16} className="text-emerald-500" />确定性治理门禁</h3>
          <div className="space-y-2">
            {rules.slice(0, 6).map(rule => (
              <div key={rule.id} className="flex items-start gap-3 px-3 py-2 bg-gray-50 rounded-lg">
                <CheckCircle2 size={15} className={rule.enabled ? 'text-emerald-500 mt-0.5' : 'text-gray-300 mt-0.5'} />
                <div className="flex-1"><p className="text-sm font-medium">{rule.name}</p><p className="text-xs text-gray-500 mt-0.5">{rule.description}</p></div>
                {severityBadge(rule.severity)}
              </div>
            ))}
          </div>
        </Card>
        <Card>
          <h3 className="text-sm font-semibold mb-3 flex items-center gap-2"><GitBranch size={16} className="text-violet-500" />最近关系</h3>
          <div className="space-y-2">
            {edges.slice(0, 7).map(edge => (
              <div key={edge.id} className="flex items-center gap-2 text-xs border-b border-gray-50 pb-2">
                <span className="font-medium truncate max-w-[130px]">{edge.subject_name ?? edge.subject_id}</span>
                <ChevronRight size={12} className="text-gray-300" />
                <code className="text-indigo-600 bg-indigo-50 px-1.5 py-0.5 rounded">{edge.predicate}</code>
                <ChevronRight size={12} className="text-gray-300" />
                <span className="font-medium truncate">{edge.object_name ?? edge.object_id}</span>
              </div>
            ))}
          </div>
        </Card>
      </div>
    </div>
  )
}

function ModelTab({ elementsByLayer, loading, layerFilter, profileFilter, query, setLayerFilter, setProfileFilter, setQuery, onSelect, edges }: {
  elementsByLayer: Record<OntologyLayer, OntologyElement[]>
  loading: boolean
  layerFilter: string
  profileFilter: string
  query: string
  setLayerFilter: (value: string) => void
  setProfileFilter: (value: string) => void
  setQuery: (value: string) => void
  onSelect: (id: string) => void
  edges: GovernanceAssertion[]
}) {
  return (
    <div className="space-y-5">
      <Card className="p-4">
        <div className="grid md:grid-cols-[1fr_160px_180px] gap-3">
          <div className="relative"><Search size={15} className="absolute left-3 top-2.5 text-gray-400" /><input value={query} onChange={event => setQuery(event.target.value)} placeholder="搜索名称、URI 或描述" className="w-full border border-gray-300 rounded-lg pl-9 pr-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" /></div>
          <select value={layerFilter} onChange={event => setLayerFilter(event.target.value)} className="border border-gray-300 rounded-lg px-3 py-2 text-sm bg-white"><option value="">全部层级</option>{LAYERS.map(layer => <option key={layer.id} value={layer.id}>{layer.label}</option>)}</select>
          <select value={profileFilter} onChange={event => setProfileFilter(event.target.value)} className="border border-gray-300 rounded-lg px-3 py-2 text-sm bg-white"><option value="">全部 Profile</option>{(Object.keys(PROFILE_META) as OntologyProfile[]).map(profile => <option key={profile} value={profile}>{PROFILE_META[profile].label}</option>)}</select>
        </div>
      </Card>
      {loading ? <div className="py-10 flex justify-center"><Spinner /></div> : LAYERS.map(layer => (
        (!layerFilter || layerFilter === layer.id) && (
          <section key={layer.id} className={`border rounded-xl p-4 ${layer.tone}`}>
            <div className="flex items-center justify-between mb-3">
              <div><h3 className="text-sm font-semibold">{layer.label}</h3><p className="text-xs text-gray-500">{layer.description}</p></div>
              <Badge>{elementsByLayer[layer.id].length} 个元素</Badge>
            </div>
            <div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-2">
              {elementsByLayer[layer.id].map(element => (
                <button key={element.id} onClick={() => onSelect(element.id)} className="bg-white/90 border border-white rounded-lg p-3 text-left hover:shadow-sm hover:border-indigo-200 transition-all">
                  <div className="flex items-start justify-between gap-2"><span className="text-sm font-medium truncate">{element.name}</span><ChevronRight size={13} className="text-gray-300 mt-1 shrink-0" /></div>
                  <p className="text-[11px] text-gray-400 mt-1 truncate">{element.kind}</p>
                  <div className="mt-2 flex items-center justify-between">{profileChip(element.profile)}{element.status === 'candidate' ? <Badge variant="yellow">候选</Badge> : <Badge variant="green">正式</Badge>}</div>
                </button>
              ))}
              {elementsByLayer[layer.id].length === 0 && <p className="text-xs text-gray-400 py-4">当前筛选下没有元素</p>}
            </div>
          </section>
        )
      ))}
      <Card>
        <h3 className="text-sm font-semibold mb-3">同层语义与层间实例化关系</h3>
        <div className="grid md:grid-cols-2 gap-2 max-h-72 overflow-y-auto">
          {edges.map(edge => (
            <div key={edge.id} className="flex items-center gap-2 text-xs px-3 py-2 bg-gray-50 rounded-lg">
              <span className="font-medium truncate flex-1">{edge.subject_name ?? edge.subject_id}</span>
              <code className="text-indigo-600">{edge.predicate}</code>
              <span className="font-medium truncate flex-1 text-right">{edge.object_name ?? edge.object_id}</span>
              {reviewBadge(edge.review_status)}
            </div>
          ))}
        </div>
      </Card>
    </div>
  )
}

function AssertionsTab({ assertions, loading, onReview, reviewing }: {
  assertions: GovernanceAssertion[]
  loading: boolean
  onReview: (assertion: GovernanceAssertion, status: 'accepted' | 'rejected') => void
  reviewing: boolean
}) {
  if (loading) return <div className="py-10 flex justify-center"><Spinner /></div>
  return (
    <Card className="p-0 overflow-hidden">
      <div className="px-5 py-4 border-b border-gray-100"><h3 className="text-sm font-semibold">Assertion + Evidence + Time + Review</h3><p className="text-xs text-gray-500 mt-1">算法置信度不代替人工审核；inferred 关系默认进入候选区。</p></div>
      <div className="divide-y divide-gray-100">
        {assertions.map(assertion => (
          <div key={assertion.id} className="p-4">
            <div className="flex items-start gap-3">
              <div className="flex-1 min-w-0">
                <div className="flex items-center gap-2 text-sm flex-wrap">
                  <span className="font-semibold">{assertion.subject_name ?? assertion.subject_id}</span>
                  <code className="text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded">{assertion.predicate}</code>
                  <span className="font-semibold">{assertion.object_name || assertion.object_value || assertion.object_id}</span>
                </div>
                <div className="flex items-center gap-2 mt-2 text-xs text-gray-500 flex-wrap">
                  <Badge variant={assertion.assertion_type === 'inferred' ? 'yellow' : 'blue'}>{assertion.assertion_type}</Badge>
                  {reviewBadge(assertion.review_status)}
                  <span>置信度 {(assertion.confidence * 100).toFixed(0)}%</span>
                  <span>证据 {assertion.evidence_ids.length}</span>
                  {assertion.generated_by && <span className="truncate max-w-sm">生成者 {assertion.generated_by}</span>}
                </div>
              </div>
              {assertion.review_status === 'pending' && (
                <div className="flex gap-2 shrink-0">
                  <Button size="sm" loading={reviewing} onClick={() => onReview(assertion, 'accepted')}>接受</Button>
                  <Button size="sm" variant="danger" disabled={reviewing} onClick={() => onReview(assertion, 'rejected')}>拒绝</Button>
                </div>
              )}
            </div>
          </div>
        ))}
        {assertions.length === 0 && <EmptyState icon={<FileCheck2 size={40} />} title="暂无断言" />}
      </div>
    </Card>
  )
}

function IssuesTab({ issues, loading, onUpdate }: {
  issues: GovernanceIssue[]
  loading: boolean
  onUpdate: (issue: GovernanceIssue, status: GovernanceIssue['status']) => void
}) {
  if (loading) return <div className="py-10 flex justify-center"><Spinner /></div>
  if (issues.length === 0) return <Card><EmptyState icon={<CheckCircle2 size={44} />} title="没有开放的高优先级治理问题" description="再次执行治理门禁可刷新检查结果。" /></Card>
  return (
    <div className="space-y-3">
      {issues.map(issue => (
        <Card key={issue.id} className="p-4">
          <div className="flex items-start gap-3">
            <AlertTriangle size={18} className={issue.severity === 'critical' ? 'text-red-500 mt-0.5' : 'text-amber-500 mt-0.5'} />
            <div className="flex-1"><div className="flex items-center gap-2"><h3 className="text-sm font-semibold">{issue.title}</h3>{severityBadge(issue.severity)}</div><p className="text-sm text-gray-600 mt-1">{issue.description}</p><p className="text-xs text-gray-400 mt-2">规则 {issue.rule_id} · 证据 {issue.evidence_ids.length}</p></div>
            <div className="flex gap-2"><Button variant="secondary" size="sm" onClick={() => onUpdate(issue, 'acknowledged')}>认领</Button><Button size="sm" onClick={() => onUpdate(issue, 'resolved')}>已整改</Button></div>
          </div>
        </Card>
      ))}
    </div>
  )
}

function EvidenceTab({ evidence, loading }: { evidence: GovernanceEvidence[]; loading: boolean }) {
  if (loading) return <div className="py-10 flex justify-center"><Spinner /></div>
  if (evidence.length === 0) return <Card><EmptyState icon={<FileCheck2 size={44} />} title="暂无治理证据" description="运行智能抽取或通过 API 接入源码、配置、运行观测和审批记录。" /></Card>
  return (
    <div className="grid md:grid-cols-2 gap-4">
      {evidence.map(item => (
        <Card key={item.id} className="p-4">
          <div className="flex items-start gap-3">
            <div className="w-9 h-9 rounded-lg bg-emerald-50 text-emerald-600 flex items-center justify-center shrink-0"><FileCheck2 size={17} /></div>
            <div className="min-w-0 flex-1">
              <div className="flex items-center gap-2"><h3 className="text-sm font-semibold truncate">{item.title}</h3><Badge variant="green">{item.kind}</Badge></div>
              <p className="text-xs text-gray-500 mt-2 line-clamp-2">{item.content || '仅保存外部证据定位符'}</p>
              <div className="mt-3 text-[11px] text-gray-400 space-y-1">
                <p className="truncate">来源：{item.source || item.uri || '未填写'}</p>
                <p>观测时间：{item.observed_at}</p>
                <p className="font-mono truncate">SHA-256 {item.content_hash}</p>
              </div>
            </div>
          </div>
        </Card>
      ))}
    </div>
  )
}

function AgentsTab({ showAnalyze, setShowAnalyze, sourceType, setSourceType, sourceName, setSourceName, environment, setEnvironment, content, setContent, onAnalyze, analyzing, lastResult, runs }: {
  showAnalyze: boolean
  setShowAnalyze: (value: boolean) => void
  sourceType: string
  setSourceType: (value: string) => void
  sourceName: string
  setSourceName: (value: string) => void
  environment: string
  setEnvironment: (value: string) => void
  content: string
  setContent: (value: string) => void
  onAnalyze: () => void
  analyzing: boolean
  lastResult: null | { run_id: string; result: { summary: { elements: number; assertions: number; issues: number }; agents: string[] }; trace: Array<{ term_name: string; duration_ms: number }> }
  runs: Array<{ id: string; source_type: string; status: string; trace_json: Array<{ term_name: string; duration_ms: number }>; error: string; created_at: string }>
}) {
  return (
    <div className="space-y-5">
      <Card className="border-indigo-200 bg-gradient-to-br from-indigo-50 to-white">
        <div className="flex items-start justify-between gap-4">
          <div><div className="flex items-center gap-2"><Bot size={18} className="text-indigo-600" /><h3 className="font-semibold">LambdAgent 治理抽取流水线</h3></div><p className="text-sm text-gray-600 mt-2 max-w-3xl">来源标准化后，资产发现、业务语义和治理控制三个智能体并行工作，再由综合智能体合并；确定性事实自动附证据，推断映射进入人工审核。</p></div>
          <Button icon={<Play size={14} />} onClick={() => setShowAnalyze(!showAnalyze)}>{showAnalyze ? '收起' : '运行抽取'}</Button>
        </div>
        {showAnalyze && (
          <div className="mt-5 pt-5 border-t border-indigo-100 space-y-4">
            <div className="grid md:grid-cols-3 gap-3">
              <label className="text-sm"><span className="block text-xs font-medium text-gray-600 mb-1">来源类型</span><select value={sourceType} onChange={event => setSourceType(event.target.value)} className="w-full border border-gray-300 rounded-lg px-3 py-2 bg-white"><option value="ddl">DDL</option><option value="openapi">OpenAPI</option><option value="inventory">JSON Inventory</option></select></label>
              <Input label="来源名称" value={sourceName} onChange={event => setSourceName(event.target.value)} />
              <Input label="环境" value={environment} onChange={event => setEnvironment(event.target.value)} />
            </div>
            <Textarea label="待分析内容" rows={12} value={content} onChange={event => setContent(event.target.value)} />
            <div className="flex justify-end"><Button loading={analyzing} disabled={!content.trim()} icon={<Sparkles size={14} />} onClick={onAnalyze}>启动多智能体分析并入候选图</Button></div>
          </div>
        )}
      </Card>
      {lastResult && (
        <Card>
          <h3 className="text-sm font-semibold mb-3">本次运行结果 · {lastResult.run_id}</h3>
          <div className="grid grid-cols-3 gap-3 mb-4">
            <div className="bg-blue-50 rounded-lg p-3"><p className="text-xs text-blue-600">元素</p><p className="text-xl font-bold">{lastResult.result.summary.elements}</p></div>
            <div className="bg-violet-50 rounded-lg p-3"><p className="text-xs text-violet-600">断言</p><p className="text-xl font-bold">{lastResult.result.summary.assertions}</p></div>
            <div className="bg-amber-50 rounded-lg p-3"><p className="text-xs text-amber-600">问题</p><p className="text-xl font-bold">{lastResult.result.summary.issues}</p></div>
          </div>
          <div className="flex flex-wrap gap-2">{lastResult.trace.map((trace, index) => <Badge key={`${trace.term_name}-${index}`} variant="purple">β {trace.term_name} · {trace.duration_ms.toFixed(0)}ms</Badge>)}</div>
        </Card>
      )}
      <Card className="p-0 overflow-hidden">
        <div className="px-5 py-4 border-b border-gray-100"><h3 className="text-sm font-semibold">运行审计</h3></div>
        <div className="divide-y divide-gray-100">
          {runs.map(run => (
            <div key={run.id} className="px-5 py-3 flex items-center gap-3"><Bot size={15} className="text-indigo-400" /><div className="flex-1"><p className="text-sm font-medium">{run.source_type.toUpperCase()} · {run.id}</p><p className="text-xs text-gray-400">{run.created_at} · {run.trace_json.length} 个 β 规约步骤</p></div>{run.status === 'completed' ? <Badge variant="green">已完成</Badge> : <Badge variant="red">失败</Badge>}</div>
          ))}
          {runs.length === 0 && <EmptyState icon={<Bot size={40} />} title="暂无智能体运行" />}
        </div>
      </Card>
    </div>
  )
}

function Modal({ title, onClose, wide = false, children }: { title: string; onClose: () => void; wide?: boolean; children: React.ReactNode }) {
  return (
    <div className="fixed inset-0 z-50 bg-black/30 flex items-center justify-center p-4" onClick={onClose}>
      <div className={`bg-white rounded-2xl shadow-xl w-full ${wide ? 'max-w-3xl' : 'max-w-lg'} p-5`} onClick={event => event.stopPropagation()}>
        <div className="flex items-center justify-between mb-5"><h2 className="text-base font-semibold">{title}</h2><button onClick={onClose} className="text-gray-400 hover:text-gray-600"><X size={18} /></button></div>
        {children}
      </div>
    </div>
  )
}
