/**
 * TaskModal v2.0 — 任务详情面板（真实 API 对接版）
 * 数据来源：store.v2taskDetail（通过 loadV2TaskDetail 从后端 expand=all API 加载）
 */
import { useEffect, useState } from 'react';
import { useStore } from '../store';
import { api } from '../api';
import CheckpointPanel from './CheckpointPanel';
import ArtifactPanel from './ArtifactPanel';

// ── 常量 ──
const STATUS_META: Record<string, { color: string; label: string }> = {
  pending:       { color: '#7a9aff', label: '待认领' },
  claimed:       { color: '#a07aff', label: '已认领' },
  working:       { color: '#6a9eff', label: '执行中' },
  review:        { color: '#818cf8', label: '审查中' },
  paused:        { color: '#818cf8', label: '已暂停' },
  escalated:     { color: '#ff5270', label: '已升级' },
  waiting_human: { color: '#f59e0b', label: '等人工' },
  done:          { color: '#2ecc8a', label: '已完成' },
  failed:        { color: '#ff5270', label: '失败' },
  blocked:       { color: '#f59e0b', label: '阻塞' },
  cancelled:     { color: '#6b7280', label: '已取消' },
};

const VALID_TRANSITIONS: Record<string, string[]> = {
  pending:       ['claimed', 'paused', 'blocked', 'cancelled'],
  claimed:       ['working', 'pending', 'paused', 'cancelled'],
  working:       ['review', 'blocked', 'failed', 'paused', 'escalated', 'waiting_human', 'cancelled'],
  paused:        ['working', 'claimed', 'review', 'escalated', 'waiting_human', 'cancelled'],
  review:        ['done', 'pending', 'failed', 'paused', 'escalated', 'waiting_human', 'cancelled'],
  escalated:     ['working', 'pending', 'paused', 'cancelled'],
  waiting_human: ['working', 'done', 'paused', 'cancelled'],
  blocked:       ['pending', 'escalated', 'cancelled'],
  failed:        ['pending', 'escalated', 'cancelled'],
  done:          ['cancelled'],
  cancelled:     ['pending'],
};

const PRIORITY_META: Record<number, { color: string; label: string }> = {
  1: { color: '#6b7280', label: '低' }, 2: { color: '#6b7280', label: '低' },
  3: { color: '#3b82f6', label: '中' }, 4: { color: '#3b82f6', label: '中' },
  5: { color: '#f59e0b', label: '高' }, 6: { color: '#f59e0b', label: '高' },
  7: { color: '#ff5270', label: '紧急' }, 8: { color: '#ff5270', label: '紧急' },
  9: { color: '#ff5270', label: '紧急' }, 10: { color: '#ff5270', label: '紧急' },
};

const AGENT_EMOJI: Record<string, string> = {
  'pangtong-fujunshi': '🐦', 'simayi-challenger': '🦅', 'jiangwei-infra': '🔧',
  'guanyu-dev': '⚔️', 'zhangfei-dev': '💪', 'zhaoyun-data': '📊',
};

const COMMENT_TYPE_LABEL: Record<string, { icon: string; label: string; color: string }> = {
  handoff: { icon: '🤝', label: '交接', color: '#6a9eff' },
  progress: { icon: '🔄', label: '进展', color: '#2ecc8a' },
  review: { icon: '🔍', label: '审查', color: '#f59e0b' },
  rebuttal: { icon: '⚔️', label: '反驳', color: '#818cf8' },
  debate: { icon: '🏛️', label: '辩论', color: '#ec4899' },
  observation: { icon: '👁️', label: '观察', color: '#6b7280' },
  general: { icon: '💬', label: '评论', color: '#6b7280' },
};

const EVENT_TYPE_ICON: Record<string, string> = {
  task_created: '📋', task_claimed: '👤', task_started: '⚔️', task_completed: '✅',
  task_failed: '❌', comment_added: '💬', output_written: '📦', review_submitted: '🔍',
  review_approved: '✅', review_rejected: '❌', observation_added: '👁️', decision_made: '🧭',
  status_changed: '🔄', task_blocked: '🚧',
};

function fmtTime(iso: string): string {
  try {
    const d = new Date(iso.includes('T') ? iso : iso.replace(' ', 'T') + 'Z');
    return `${d.getMonth()+1}/${d.getDate()} ${d.getHours()}:${String(d.getMinutes()).padStart(2,'0')}`;
  } catch { return iso; }
}

function fmtTimeFull(iso: string): string {
  try {
    const d = new Date(iso.includes('T') ? iso : iso.replace(' ', 'T') + 'Z');
    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${d.getHours()}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`;
  } catch { return iso; }
}

// ── 子组件 ──
function SectionLabel({ icon, title, count }: { icon: string; title: string; count?: number }) {
  return (
    <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)', marginBottom: 8, display: 'flex', alignItems: 'center', gap: 6 }}>
      <span>{icon}</span><span>{title}</span>
      {count !== undefined && <span style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 400 }}>({count})</span>}
    </div>
  );
}

function StatusButtons({ status, taskId, resumedFrom, onAction }: { status: string; taskId: string; resumedFrom?: string; onAction?: () => void }) {
  const toast = useStore((s) => s.toast);
  const loadAll = useStore((s) => s.loadAll);
  const loadV2TaskDetail = useStore((s) => s.loadV2TaskDetail);
  const selectedProjectId = useStore((s) => s.selectedProjectId);
  const projects = useStore((s) => s.projects);
  const loadV2Tasks = useStore((s) => s.loadV2Tasks);
  const [loading, setLoading] = useState<string | null>(null);

  // AI Native：只显示人需要做的动作
  // v3.1: 取消 ADVANCED 折叠区，所有操作直接展示
  // 设计原则：暂停和取消是用户通用权利
  const ACTION_BUTTONS: Record<string, Array<{ target: string; label: string; icon: string; bg: string; color: string; border: string }>> = {
    pending: [
      { target: 'paused', label: '暂停', icon: '⏸', bg: '#818cf822', color: '#818cf8', border: '#818cf844' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    claimed: [
      { target: 'paused', label: '暂停', icon: '⏸', bg: '#818cf822', color: '#818cf8', border: '#818cf844' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    working: [
      { target: 'paused', label: '暂停', icon: '⏸', bg: '#818cf822', color: '#818cf8', border: '#818cf844' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    paused: [
      { target: resumedFrom || 'working', label: '恢复', icon: '▶', bg: '#2ecc8a22', color: '#2ecc8a', border: '#2ecc8a44' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    review: [
      { target: 'paused', label: '暂停', icon: '⏸', bg: '#818cf822', color: '#818cf8', border: '#818cf844' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    failed: [
      { target: 'pending', label: '重试', icon: '🔄', bg: '#7a9aff22', color: '#7a9aff', border: '#7a9aff44' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    blocked: [
      { target: 'pending', label: '解除阻塞', icon: '🔓', bg: '#2ecc8a22', color: '#2ecc8a', border: '#2ecc8a44' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    escalated: [
      { target: 'working', label: '继续执行', icon: '▶', bg: '#2ecc8a22', color: '#2ecc8a', border: '#2ecc8a44' },
      { target: 'pending', label: '重新分配', icon: '🔄', bg: '#7a9aff22', color: '#7a9aff', border: '#7a9aff44' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    waiting_human: [
      { target: 'done', label: '确认', icon: '✅', bg: '#2ecc8a22', color: '#2ecc8a', border: '#2ecc8a44' },
      { target: 'working', label: '拒绝', icon: '🔄', bg: '#7a9aff22', color: '#7a9aff', border: '#7a9aff44' },
      { target: 'cancelled', label: '取消', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    done: [
      { target: '__archive', label: '归档', icon: '📦', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
    cancelled: [
      { target: 'pending', label: '重新启动', icon: '🔄', bg: '#2ecc8a22', color: '#2ecc8a', border: '#2ecc8a44' },
      { target: '__archive', label: '归档', icon: '📦', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
    ],
  };

  const buttons = ACTION_BUTTONS[status] || [];

  const handleClick = async (targetStatus: string) => {
    setLoading(targetStatus);
    try {
      if (targetStatus === '__archive') {
        const pid = selectedProjectId || '';
        const res = await fetch(`/api/projects/${pid}/tasks/${taskId}`, {
          method: 'PATCH', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ archived: 1 }),
        });
        if (res.ok) { toast('📦 已归档'); loadV2Tasks(); } else toast('归档失败', 'err');
      } else {
        const pid = selectedProjectId || '';
        const res = await fetch(`/api/projects/${pid}/tasks/${taskId}/status`, {
          method: 'POST', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ status: targetStatus }),
        });
        const result = await res.json();
        if (result.ok) { toast(`${targetStatus} 操作成功`, 'ok'); loadV2TaskDetail(taskId); loadAll(); onAction?.(); }
        else toast(result.detail?.detail || result.error || '操作失败', 'err');
      }
    } catch (e: any) {
      toast(e.message || '⚠️ 操作失败', 'err');
    } finally {
      setLoading(null);
    }
  };

  const btn = (b: typeof buttons[0]) => (
    <button key={b.target} onClick={() => handleClick(b.target)} disabled={!!loading} style={{
      padding: '5px 12px', borderRadius: 6, fontSize: 11, cursor: loading ? 'not-allowed' : 'pointer',
      background: b.bg, color: b.color, border: `1px solid ${b.border}`, fontWeight: 600, opacity: loading === b.target ? 0.6 : 1,
    }}>{loading === b.target ? '⏳' : b.icon} {b.label}</button>
  );

  if (buttons.length === 0) {
    return <div style={{ fontSize: 12, color: 'var(--muted)', padding: '4px 0' }}>-</div>;
  }

  return (
    <div>
      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>{buttons.map(b => btn(b))}</div>
    </div>
  );
}

function EventTimeline({ events }: { events: any[] }) {
  if (!events || events.length === 0) return <div style={{ fontSize: 12, color: 'var(--muted)', textAlign: 'center', padding: 16 }}>暂无事件记录</div>;
  const sorted = [...events].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
      {sorted.map((ev, i) => (
        <div key={ev.id || i} style={{ display: 'flex', gap: 8, padding: '6px 0', position: 'relative' }}>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: 24, flexShrink: 0 }}>
            <span style={{ fontSize: 13 }}>{EVENT_TYPE_ICON[ev.event_type] || '📌'}</span>
            {i < sorted.length - 1 && <div style={{ width: 1, flex: 1, background: 'var(--line)', marginTop: 2 }} />}
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <span style={{ fontSize: 12, fontWeight: 500 }}>{ev.detail || ev.event_type}</span>
              {ev.created_at && <span style={{ fontSize: 10, color: 'var(--muted)', flexShrink: 0, marginLeft: 8 }}>{fmtTime(ev.created_at)}</span>}
            </div>
            {ev.agent && <span style={{ fontSize: 10, color: 'var(--muted)' }}>{AGENT_EMOJI[ev.agent] || ''} {ev.agent}</span>}
          </div>
        </div>
      ))}
    </div>
  );
}

// ── v2.7: 子 Task 面板 ──

function SubtaskPanel({ taskId, stagesJson }: { taskId: string; stagesJson?: string }) {
  const [subtasks, setSubtasks] = useState<any[]>([]);
  const [progress, setProgress] = useState<any>(null);

  useEffect(() => {
    let mounted = true;
    (async () => {
      const [st, pg] = await Promise.all([
        api.listSubtasks(taskId),
        api.taskProgress(taskId),
      ]);
      if (mounted) {
        setSubtasks(st as any[]);
        setProgress(pg);
      }
    })();
    return () => { mounted = false; };
  }, [taskId]);

  if (subtasks.length === 0 && !progress?.total_subtasks) return null;

  const stages = progress?.stages || [];
  const total = progress?.total_subtasks || subtasks.length;
  const done = progress?.done_subtasks || 0;
  const activeStage = progress?.active_stage;

  return (
    <div style={{ marginBottom: 16 }}>
      <SectionLabel icon="📊" title={`子任务进度 ${done}/${total}`} />

      {/* Stage 进度条 */}
      {stages.length > 0 && (
        <div style={{ display: 'flex', gap: 2, marginBottom: 8, padding: '6px 8px', background: 'var(--panel2)', borderRadius: 8 }}>
          {stages.map((s: any) => {
            const pct = s.total > 0 ? s.done / s.total : 0;
            const isActive = s.active > 0 || (s.total > 0 && s.done < s.total && s.label === activeStage);
            return (
              <div key={s.id} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
                <div style={{ width: '100%', height: 4, background: '#1c2236', borderRadius: 2, overflow: 'hidden' }}>
                  <div style={{
                    width: `${pct * 100}%`, height: '100%', borderRadius: 2,
                    background: pct >= 1 ? '#2ecc8a' : isActive ? '#6a9eff' : '#f59e0b',
                    transition: 'width .3s',
                  }} />
                </div>
                <span style={{ fontSize: 9, color: isActive ? '#6a9eff' : pct >= 1 ? '#2ecc8a' : 'var(--muted)', fontWeight: isActive ? 700 : 400 }}>
                  {s.label}
                </span>
              </div>
            );
          })}
        </div>
      )}

      {/* 子 Task 列表 */}
      {subtasks.length > 0 && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          {subtasks.map((st: any) => {
            const ssm = STATUS_META[st.status] || STATUS_META.pending;
            const emoji = st.org ? (AGENT_EMOJI[st.org] || '🤖') : '';
            return (
              <div key={st.id} style={{
                display: 'flex', alignItems: 'center', gap: 8,
                padding: '6px 10px', background: 'var(--panel2)', borderRadius: 6,
                fontSize: 12,
              }}>
                <span style={{ fontSize: 9, padding: '1px 5px', borderRadius: 3, border: `1px solid ${ssm.color}44`, color: ssm.color, background: (ssm as any).bg || '#141824' }}>{ssm.label}</span>
                <span style={{ flex: 1, color: '#dde4f8', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{st.title}</span>
                {st.stage && <span style={{ fontSize: 10, color: '#f59e0b' }}>{st.stage}</span>}
                {emoji && <span style={{ fontSize: 11 }}>{emoji}</span>}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ── 主组件 ──
export default function TaskModal() {
  const modalTaskId = useStore(s => s.modalTaskId);
  const setModalTaskId = useStore(s => s.setModalTaskId);
  const loadV2TaskDetail = useStore(s => s.loadV2TaskDetail);
  const taskDetail = useStore(s => s.v2taskDetail);
  const toast = useStore(s => s.toast);
  const selectedProjectId = useStore(s => s.selectedProjectId);
  const projects = useStore(s => s.projects);
  const [activeTab, setActiveTab] = useState<'overview' | 'outputs' | 'reviews' | 'experience'>('overview');

  // 打开时加载详情
  useEffect(() => {
    if (modalTaskId) {
      setActiveTab('overview');
      loadV2TaskDetail(modalTaskId);
    }
  }, [modalTaskId]);

  if (!modalTaskId) return null;

  const task = taskDetail;
  if (!task) {
    return (
      <div style={{ position: 'fixed', inset: 0, zIndex: 1000, background: 'rgba(0,0,0,0.6)', display: 'flex', justifyContent: 'center', alignItems: 'center' }} onClick={() => setModalTaskId(null)}>
        <div style={{ background: 'var(--bg)', border: '1px solid var(--line)', borderRadius: 16, padding: 40, textAlign: 'center' }}>
          <div style={{ fontSize: 24, marginBottom: 8 }}>⏳</div>
          <div style={{ fontSize: 14, color: 'var(--muted)' }}>加载中...</div>
        </div>
      </div>
    );
  }

  const sm = STATUS_META[task.status] || STATUS_META.pending;
  const pm = PRIORITY_META[task.priority] || { color: '#6b7280', label: `P${task.priority}` };
  const close = () => setModalTaskId(null);

  const comments = (task as any).comments || [];
  const outputs = (task as any).outputs || [];
  const reviews = (task as any).reviews || [];
  const decisions = (task as any).decisions || [];
  const events = (task as any).events || [];
  const experiences = (task as any).experiences || [];

  const tabs = [
    { key: 'overview' as const, label: '📋 总览' },
    { key: 'outputs' as const, label: '📦 产出', badge: outputs.length },
    { key: 'reviews' as const, label: '🔍 审查', badge: reviews.length },
    { key: 'experience' as const, label: '🧠 经验', badge: experiences.length },
  ];

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 1000,
      background: 'rgba(0,0,0,0.6)', display: 'flex', justifyContent: 'center', alignItems: 'flex-start',
      padding: '40px 20px', overflowY: 'auto', backdropFilter: 'blur(4px)',
    }} onClick={close}>
      <div style={{
        background: 'var(--bg)', border: '1px solid var(--line)', borderRadius: 16,
        width: '100%', maxWidth: 720, maxHeight: 'calc(100vh - 80px)', overflow: 'hidden',
        display: 'flex', flexDirection: 'column', boxShadow: '0 8px 40px rgba(0,0,0,0.5)',
      }} onClick={e => e.stopPropagation()}>

        {/* Header */}
        <div style={{ padding: '16px 20px', borderBottom: '1px solid var(--line)', flexShrink: 0 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                <span style={{ fontSize: 11, color: 'var(--acc)', fontWeight: 700, letterSpacing: '.04em' }}>{task.id}</span>
                <span style={{ fontSize: 10, padding: '2px 6px', borderRadius: 4, background: sm.color + '22', color: sm.color, fontWeight: 600 }}>{sm.label}</span>
                <span style={{ fontSize: 10, padding: '2px 6px', borderRadius: 4, background: pm.color + '22', color: pm.color, fontWeight: 600 }}>{pm.label}优先级</span>
              </div>
              <div style={{ fontSize: 18, fontWeight: 700, lineHeight: 1.3 }}>{task.title}</div>
            </div>
            <button onClick={close} style={{ background: 'none', border: 'none', color: 'var(--muted)', fontSize: 18, cursor: 'pointer', padding: 4 }}>✕</button>
          </div>
          <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 6 }}>
            {task.assignee && <span>{AGENT_EMOJI[task.assignee] || '🤖'} {task.assignee}</span>}
            <span style={{ margin: '0 8px' }}>·</span>
            <span>创建于 {fmtTimeFull(task.created_at)}</span>
            <span style={{ margin: '0 8px' }}>·</span>
            <span>更新于 {fmtTime(task.updated_at)}</span>
            <span style={{ margin: '0 8px' }}>·</span>
            <ProjectSelector taskId={task.id} currentProject={(task as any)?._projectId || selectedProjectId} projects={projects} />
          </div>
        </div>

        {/* Tabs */}
        <div style={{ display: 'flex', borderBottom: '1px solid var(--line)', padding: '0 20px', flexShrink: 0 }}>
          {tabs.map(tab => (
            <button key={tab.key} onClick={() => setActiveTab(tab.key)} style={{
              padding: '10px 16px', fontSize: 12, fontWeight: 600,
              border: 'none', background: 'none', cursor: 'pointer',
              color: activeTab === tab.key ? 'var(--acc)' : 'var(--muted)',
              borderBottom: activeTab === tab.key ? '2px solid var(--acc)' : '2px solid transparent',
              display: 'flex', alignItems: 'center', gap: 4, transition: 'all .15s',
            }}>
              {tab.label}
              {tab.badge ? <span style={{
                fontSize: 9, background: activeTab === tab.key ? 'var(--acc)' : '#2a3550',
                color: activeTab === tab.key ? '#000' : '#8899aa',
                borderRadius: 8, padding: '0 5px', minWidth: 16, textAlign: 'center',
              }}>{tab.badge}</span> : null}
            </button>
          ))}
        </div>

        {/* Tab Content */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '16px 20px' }}>

          {/* ── 总览 ── */}
          {activeTab === 'overview' && (<>
            {/* 描述 */}
            {task.description && (
              <div style={{ marginBottom: 16 }}>
                <SectionLabel icon="📝" title="需求描述" />
                <div style={{ fontSize: 12, color: '#a0aec0', lineHeight: 1.6, padding: '8px 12px', background: 'var(--panel2)', borderRadius: 8 }}>{task.description}</div>
              </div>
            )}

            {/* 状态操作 */}
            <div style={{ marginBottom: 16 }}>
              <SectionLabel icon="🔄" title="状态操作" />
              <StatusButtons status={task.status} taskId={task.id} resumedFrom={task.resumed_from ?? undefined} />
            </div>

            {/* v2.7: 子 Task 进度 + 列表 */}
            <SubtaskPanel taskId={task.id} stagesJson={task.stages_json} />

            {/* 任务信息 */}
            <div style={{ marginBottom: 16 }}>
              <SectionLabel icon="📊" title="任务信息" />
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                {[
                  ['任务类型', task.task_type || '—'],
                  ['优先级', `${task.priority} (${pm.label})`],
                  ['风险等级', task.risk_level || '—'],
                  ['重试次数', `${task.retry_count}/${task.max_retries}`],
                  ['认领时间', task.claimed_at ? fmtTime(task.claimed_at) : '—'],
                  ['开始时间', task.started_at ? fmtTime(task.started_at) : '—'],
                  ['截止时间', task.deadline ? fmtTime(task.deadline) : '无'],
                  ['分配人', task.assigned_by ? `${AGENT_EMOJI[task.assigned_by] || ''} ${task.assigned_by}` : '—'],
                ].map(([k, v], i) => (
                  <div key={i} style={{ display: 'flex', gap: 6, fontSize: 11 }}>
                    <span style={{ color: 'var(--muted)', minWidth: 56 }}>{k}</span>
                    <span style={{ color: 'var(--text)' }}>{v}</span>
                  </div>
                ))}
              </div>
            </div>

            {/* 事件时间线 */}
            <div style={{ marginBottom: 16 }}>
              <SectionLabel icon="📜" title="事件时间线" count={events.length} />
              <div style={{ padding: '8px 12px', background: 'var(--panel2)', borderRadius: 8 }}>
                <EventTimeline events={events} />
              </div>
            </div>

            {/* 评论 */}
            {comments.length > 0 && (
              <div style={{ marginBottom: 16 }}>
                <SectionLabel icon="💬" title="评论/交接" count={comments.length} />
                <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                  {comments.map((c: any, i: number) => {
                    const ct = COMMENT_TYPE_LABEL[c.comment_type] || COMMENT_TYPE_LABEL.general;
                    return (
                      <div key={c.id || i} style={{ padding: '8px 12px', background: 'var(--panel2)', borderRadius: 8 }}>
                        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11 }}>
                            <span>{AGENT_EMOJI[c.author] || '🤖'}</span>
                            <span style={{ fontWeight: 600 }}>{c.author}</span>
                            <span style={{ fontSize: 9, padding: '1px 4px', borderRadius: 3, background: ct.color + '22', color: ct.color }}>{ct.icon} {ct.label}</span>
                          </div>
                          <span style={{ fontSize: 10, color: 'var(--muted)' }}>{c.created_at ? fmtTime(c.created_at) : ''}</span>
                        </div>
                        <div style={{ fontSize: 12, color: '#a0aec0', lineHeight: 1.5 }}>{c.body || c.content}</div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* 评论输入框 */}
            <div style={{ marginBottom: 16 }}>
              <SectionLabel icon="✍️" title="发表评论" />
              <div style={{ display: 'flex', gap: 6, alignItems: 'flex-start' }}>
                <select id="comment-type-select" style={{
                  fontSize: 11, padding: '6px 8px', borderRadius: 6,
                  border: '1px solid var(--line)', background: 'var(--panel2)',
                  color: 'var(--fg)', cursor: 'pointer', flexShrink: 0,
                }}>
                  <option value="general">💬 评论</option>
                  <option value="handoff">🤝 交接</option>
                  <option value="progress">📊 进展</option>
                  <option value="observation">👁 观察</option>
                </select>
                <input
                  id="comment-body-input"
                  type="text"
                  placeholder="输入评论内容..."
                  style={{
                    flex: 1, padding: '6px 10px', borderRadius: 6,
                    border: '1px solid var(--line)', background: 'var(--panel2)',
                    color: 'var(--fg)', fontSize: 12, outline: 'none',
                  }}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter') {
                      const body = (e.target as HTMLInputElement).value.trim();
                      const typeEl = document.getElementById('comment-type-select') as HTMLSelectElement;
                      if (!body || !task) return;
                      const pid = selectedProjectId;
                      if (!pid) return;
                      fetch(`/api/projects/${pid}/tasks/${task.id}/comments`, {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ author: 'user', body, comment_type: typeEl?.value || 'general' }),
                      }).then(r => r.json()).then(r => {
                        if (r.ok) {
                          (e.target as HTMLInputElement).value = '';
                          loadV2TaskDetail(task.id);
                          toast('评论已发送', 'ok');
                        } else {
                          toast(r.error || '评论失败', 'err');
                        }
                      }).catch(() => toast('评论失败', 'err'));
                    }
                  }}
                />
              </div>
            </div>

            {/* 决策记录 */}
            {decisions.length > 0 && (
              <div style={{ marginBottom: 16 }}>
                <SectionLabel icon="🧭" title="决策记录" count={decisions.length} />
                {decisions.map((d: any, i: number) => (
                  <div key={d.id || i} style={{ padding: '8px 12px', background: 'var(--panel2)', borderRadius: 8, borderLeft: '3px solid #6a9eff', marginBottom: 6 }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                      <span style={{ fontSize: 11, fontWeight: 600 }}>{AGENT_EMOJI[d.decider] || '🤖'} {d.decider}</span>
                      <span style={{ fontSize: 10, color: 'var(--muted)' }}>{d.created_at ? fmtTime(d.created_at) : ''}</span>
                    </div>
                    <div style={{ fontSize: 12, color: '#a0aec0', lineHeight: 1.5 }}>{d.rationale || d.decision}</div>
                  </div>
                ))}
              </div>
            )}

            {/* Checkpoint 面板（waiting_human 时显示）*/}
            {task.status === 'waiting_human' && (
              <div style={{ marginBottom: 16 }}>
                <SectionLabel icon="🛐" title="Checkpoint" />
                <div style={{ padding: '12px 16px', background: 'var(--panel2)', borderRadius: 8 }}>
                  <CheckpointPanel taskId={task.id} onDone={() => loadV2TaskDetail(task.id)} />
                </div>
              </div>
            )}
          </>)}

          {/* ── 产出 ── */}
          {activeTab === 'outputs' && (<>
            <SectionLabel icon="📦" title="产出物" count={outputs.length} />
            {outputs.length === 0 ? (
              <div style={{ padding: 20, textAlign: 'center', color: 'var(--muted)', fontSize: 12 }}>暂无产出物。Agent 完成后将自动提交。</div>
            ) : (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {outputs.map((o: any, i: number) => (
                  <div key={o.id || i} style={{ padding: '12px 16px', background: 'var(--panel2)', borderRadius: 8, borderLeft: '3px solid #2ecc8a' }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                      <span style={{ fontSize: 11, fontWeight: 600 }}>{AGENT_EMOJI[o.agent] || '🤖'} {o.agent}</span>
                      <span style={{ fontSize: 10, color: 'var(--muted)' }}>{o.created_at ? fmtTime(o.created_at) : ''}</span>
                    </div>
                    <div style={{ fontSize: 12, color: '#a0aec0', lineHeight: 1.6 }}>{o.summary || o.title || o.content_path || '（无摘要）'}</div>
                    {o.output_type && <div style={{ marginTop: 6 }}><span style={{ fontSize: 10, padding: '1px 5px', borderRadius: 3, background: '#2ecc8a22', color: '#2ecc8a' }}>{o.output_type}</span></div>}
                  </div>
                ))}

                {/* v2.8: ArtifactPanel 成果物面板 */}
                <div style={{ marginTop: 8 }}>
                  <ArtifactPanel taskId={task.id} isCompleted={task.status === 'done'} />
                </div>
              </div>
            )}
          </>)}

          {/* ── 审查 ── */}
          {activeTab === 'reviews' && (<>
            <SectionLabel icon="🔍" title="审查记录" count={reviews.length} />
            {reviews.length === 0 ? (
              <div style={{ padding: 20, textAlign: 'center', color: 'var(--muted)', fontSize: 12 }}>暂无审查记录。</div>
            ) : (
              reviews.map((r: any, i: number) => (
                <div key={r.id || i} style={{
                  padding: '12px 16px', background: 'var(--panel2)', borderRadius: 8, marginBottom: 8,
                  borderLeft: `3px solid ${r.verdict === 'APPROVE' ? '#2ecc8a' : '#ff5270'}`,
                }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                      <span style={{ fontSize: 11, fontWeight: 600 }}>{AGENT_EMOJI[r.reviewer] || '🤖'} {r.reviewer}</span>
                      <span style={{
                        fontSize: 10, padding: '1px 6px', borderRadius: 4, fontWeight: 600,
                        background: r.verdict === 'APPROVE' ? '#2ecc8a22' : '#ff527022',
                        color: r.verdict === 'APPROVE' ? '#2ecc8a' : '#ff5270',
                      }}>{r.verdict === 'APPROVE' ? '✅ 通过' : '❌ 驳回'}</span>
                    </div>
                    <span style={{ fontSize: 10, color: 'var(--muted)' }}>{r.created_at ? fmtTime(r.created_at) : ''}</span>
                  </div>
                  <div style={{ display: 'flex', gap: 8, marginBottom: 6, fontSize: 10, color: 'var(--muted)' }}>
                    {r.confidence != null && <span>置信度: {(r.confidence * 100).toFixed(0)}%</span>}
                    {r.round != null && <span>轮次: {r.round}</span>}
                  </div>
                  <div style={{ fontSize: 12, color: '#a0aec0', lineHeight: 1.5 }}>{r.summary || '（无摘要）'}</div>
                </div>
              ))
            )}
          </>)}

          {/* ── 经验 ── */}
          {activeTab === 'experience' && (<>
            <SectionLabel icon="🧠" title="经验沉淀" count={experiences.length} />
            {experiences.length === 0 ? (
              <div style={{ padding: 20, textAlign: 'center', color: 'var(--muted)', fontSize: 12 }}>暂无经验沉淀。任务完成后将自动触发蒸馏。</div>
            ) : (
              experiences.map((exp: any, i: number) => (
                <div key={exp.experience_id || i} style={{
                  padding: '12px 16px', background: 'var(--panel2)', borderRadius: 8, marginBottom: 8,
                  borderLeft: `3px solid ${exp.pattern_type === 'best_practice' ? '#2ecc8a' : exp.pattern_type === 'pitfall' ? '#ff5270' : '#6a9eff'}`,
                }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                    <span style={{ fontSize: 12, fontWeight: 600 }}>{exp.title}</span>
                    <span style={{ fontSize: 10, color: 'var(--muted)' }}>{exp.created_at ? fmtTime(exp.created_at) : ''}</span>
                  </div>
                  <div style={{ fontSize: 12, color: '#a0aec0', lineHeight: 1.5, marginBottom: 6 }}>{exp.summary}</div>
                  {exp.tags && (
                    <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                      {String(exp.tags).split(',').map((t: string) => (
                        <span key={t} style={{ fontSize: 9, padding: '1px 5px', borderRadius: 3, background: '#6a9eff22', color: '#6a9eff' }}>{t.trim()}</span>
                      ))}
                    </div>
                  )}
                </div>
              ))
            )}
          </>)}
        </div>
      </div>
    </div>
  );
}

// 项目归属选择器
function ProjectSelector({ taskId, currentProject, projects }: {
  taskId: string; currentProject: string | null; projects: Record<string, any>;
}) {
  const [moving, setMoving] = useState(false);
  const pid = currentProject || '';
  const projName = projects[pid]?.name || pid || '未知项目';

  const handleMove = async (targetPid: string) => {
    if (targetPid === pid || !pid) return;
    setMoving(true);
    try {
      const res = await fetch(`/api/projects/${pid}/tasks/${taskId}/move`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ target_project_id: targetPid }),
      });
      if (res.ok) {
        const toast = useStore.getState().toast;
        toast(`✅ 已移动到 ${projects[targetPid]?.name || targetPid}`);
        useStore.getState().loadV2Tasks();
        useStore.getState().setModalTaskId(null); // 关闭 TaskModal
      } else {
        const d = await res.json();
        useStore.getState().toast(d.detail || '移动失败', 'err');
      }
    } catch {
      useStore.getState().toast('网络错误', 'err');
    }
    setMoving(false);
  };

  const otherProjects = Object.entries(projects).filter(([p]) => p !== pid && !p.startsWith('_'));

  return (
    <span style={{ position: 'relative', display: 'inline-block' }}>
      <span style={{ cursor: 'pointer', borderBottom: '1px dashed var(--muted)' }} title="点击切换项目归属">
        📁 {projName}
      </span>
      <select
        disabled={moving}
        onChange={(e) => { if (e.target.value) handleMove(e.target.value); }}
        defaultValue=""
        style={{
          position: 'absolute', inset: 0, opacity: 0, cursor: 'pointer',
          fontSize: 12, border: 'none', background: 'transparent',
        }}
      >
        <option value="" disabled>移动到...</option>
        {otherProjects.map(([p, info]) => (
          <option key={p} value={p}>{info.name || p}</option>
        ))}
      </select>
    </span>
  );
}
