/**
 * EdictBoard v2.0 — 任务看板（真实 API 对接版）
 * 数据来源：store.v2tasks（通过 loadV2Tasks 从后端 API 加载）
 * 保留 mock fallback（无项目/无数据时展示示例）
 */
import { useEffect, useState } from 'react';
import { useStore } from '../store';

// ── 状态管线（5 步线性 + blocked/failed 旁路）──
const PIPELINE_STEPS = [
  { key: 'pending', label: '待认领', icon: '📋' as string },
  { key: 'claimed', label: '已认领', icon: '👤' as string },
  { key: 'working', label: '执行中', icon: '⚔️' as string },
  { key: 'review', label: '审查中', icon: '🔍' as string },
  { key: 'done', label: '已完成', icon: '✅' as string },
];

const PIPELINE_ORDER: Record<string, number> = {
  pending: 0, claimed: 1, working: 2, review: 3, done: 4,
  paused: 2, escalated: 2, waiting_human: 2,
  failed: 2, blocked: 2, cancelled: -1,
};

const STATUS_META: Record<string, { color: string; bg: string; label: string }> = {
  pending:       { color: '#7a9aff', bg: '#0a1028', label: '待认领' },
  claimed:       { color: '#a07aff', bg: '#110a28', label: '已认领' },
  working:       { color: '#6a9eff', bg: '#0a1530', label: '执行中' },
  review:        { color: '#818cf8', bg: '#0f0a28', label: '审查中' },
  paused:        { color: '#818cf8', bg: '#110a20', label: '已暂停' },
  escalated:     { color: '#ff5270', bg: '#200a10', label: '已升级' },
  waiting_human: { color: '#f59e0b', bg: '#1a1508', label: '等人工' },
  done:          { color: '#2ecc8a', bg: '#0a2018', label: '已完成' },
  failed:        { color: '#ff5270', bg: '#200a10', label: '失败' },
  blocked:       { color: '#f59e0b', bg: '#1a1508', label: '阻塞' },
  cancelled:     { color: '#6b7280', bg: '#141824', label: '已取消' },
};

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 RISK_META: Record<string, { color: string; label: string }> = {
  low: { color: '#2ecc8a', label: '低风险' }, standard: { color: '#6b7280', label: '标准' },
  high: { color: '#f59e0b', label: '高风险' }, critical: { color: '#ff5270', label: '极高风险' },
};

const REVIEW_META: Record<string, { color: string; label: string; icon: string }> = {
  none: { color: '#6b7280', label: '', icon: '' },
  pending: { color: '#f59e0b', label: '审查中', icon: '🔍' },
  APPROVE: { color: '#2ecc8a', label: '已通过', icon: '✅' },
  REJECT: { color: '#ff5270', label: '已驳回', icon: '❌' },
  REBUTTAL: { color: '#818cf8', label: '反驳中', icon: '⚔️' },
};

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

// V2Task type (import from store)
type V2Task = {
  id: string; title: string; description: string | null;
  status: string; assignee: string | null; assigned_by: string | null;
  depends_on: string | null; parent_task: string | null;
  priority: number; task_type: string | null;
  created_at: string; updated_at: string;
  claimed_at: string | null; completed_at: string | null; started_at: string | null;
  deadline: string | null; retry_count: number; max_retries: number;
  risk_level: string | null; escalated: number;
  archived: number;
  comments_count?: number; outputs_count?: number;
  review_status?: string | null; latest_event_detail?: string | null;
  stage?: string | null; stages_json?: string;
};

// ── 工具函数 ──
function timeAgo(iso: string): string {
  try {
    const d = new Date(iso.includes('T') ? iso : iso.replace(' ', 'T') + 'Z');
    const diff = Date.now() - d.getTime();
    const mins = Math.floor(diff / 60000);
    if (mins < 1) return '刚刚';
    if (mins < 60) return `${mins}分钟前`;
    const hrs = Math.floor(mins / 60);
    if (hrs < 24) return `${hrs}小时前`;
    return `${Math.floor(hrs / 24)}天前`;
  } catch { return ''; }
}

function formatDeadline(iso: string): string {
  const diff = new Date(iso).getTime() - Date.now();
  if (diff < 0) return '已逾期';
  const hrs = Math.floor(diff / 3600000);
  if (hrs < 1) return `${Math.floor(diff / 60000)}分钟后`;
  if (hrs < 24) return `${hrs}小时后`;
  return `${Math.floor(hrs / 24)}天后`;
}

function getPriorityLabel(p: number): { color: string; label: string } {
  return PRIORITY_META[p] || { color: '#6b7280', label: `P${p}` };
}

function parseDependsOn(raw: string | null): string[] {
  if (!raw) return [];
  try { return JSON.parse(raw); } catch { return []; }
}

// ── 状态管线组件 ──
function StatusPipeline({ status }: { status: string }) {
  const isFailed = status === 'failed';
  const isBlocked = status === 'blocked';
  const isCancelled = status === 'cancelled';
  const curIdx = PIPELINE_ORDER[status] ?? -1;

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 0 }}>
      {PIPELINE_STEPS.map((stage, i) => {
        const isDone = !isFailed && !isBlocked && !isCancelled && curIdx >= 0 && i < curIdx;
        const isActive = curIdx >= 0 && i === curIdx;
        const isFailedNode = isFailed && i === 2;
        const isBlockedNode = isBlocked && i === 2;

        let bg = 'transparent', border = 'transparent', textColor = '#6b7280', icon = stage.icon;
        if (isDone) { bg = '#0a2018'; textColor = '#2ecc8a'; icon = '✓'; }
        else if (isFailedNode) { bg = '#200a10'; border = '#ff5270'; textColor = '#ff5270'; icon = '✗'; }
        else if (isBlockedNode) { bg = '#1a1508'; border = '#f59e0b'; textColor = '#f59e0b'; icon = '🚧'; }
        else if (isActive) { bg = '#0a1530'; border = '#6a9eff'; textColor = '#6a9eff'; }

        return (
          <span key={stage.key} style={{ display: 'contents' }}>
            <div style={{
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2,
              padding: '4px 6px', borderRadius: 6, minWidth: 44,
              background: bg, border: `1px solid ${border}`,
              opacity: isDone || isActive || isFailedNode || isBlockedNode ? 1 : isCancelled ? 0.25 : 0.35,
            }}>
              <span style={{ fontSize: 13 }}>{icon}</span>
              <span style={{ fontSize: 8, whiteSpace: 'nowrap', color: textColor, fontWeight: isActive || isFailedNode || isBlockedNode ? 700 : 400 }}>
                {isFailedNode ? '失败' : isBlockedNode ? '阻塞' : stage.label}
              </span>
            </div>
            {i < PIPELINE_STEPS.length - 1 && (
              <span style={{ fontSize: 10, color: isDone ? '#2ecc8a44' : '#1c2236', padding: '0 1px' }}>›</span>
            )}
          </span>
        );
      })}
    </div>
  );
}

// ── 任务卡片 ──
function TaskCard({ task, subtaskCount, subtaskDone, activeStage, onOpen, onAction }: {
  task: V2Task; subtaskCount: number; subtaskDone: number; activeStage: string | null; onOpen: () => void; onAction: (action: string) => void;
}) {
  const sm = STATUS_META[task.status] || STATUS_META.pending;
  const pm = getPriorityLabel(task.priority);
  const rm = RISK_META[task.risk_level || 'standard'] || RISK_META.standard;
  const rvm = REVIEW_META[task.review_status || 'none'] || REVIEW_META.none;
  const agentEmoji = task.assignee ? (AGENT_EMOJI[task.assignee] || '🤖') : '';
  const deps = parseDependsOn(task.depends_on);

  return (
    <div onClick={onOpen} style={{
      background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 14,
      padding: 18, cursor: 'pointer', transition: 'border-color .15s, transform .1s, box-shadow .15s',
    }}
      onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--acc)'; e.currentTarget.style.transform = 'translateY(-2px)'; e.currentTarget.style.boxShadow = '0 4px 20px rgba(106,158,255,.1)'; }}
      onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.transform = ''; e.currentTarget.style.boxShadow = ''; }}
    >
      <StatusPipeline status={task.status} />
      <div style={{ fontSize: 10, color: 'var(--acc)', fontWeight: 700, letterSpacing: '.04em', margin: '8px 0 4px' }}>{task.id}</div>
      <div style={{ fontSize: 15, fontWeight: 700, lineHeight: 1.4, marginBottom: 10 }}>{task.title}</div>

      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', marginBottom: 8 }}>
        <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 4, border: `1px solid ${sm.color}44`, color: sm.color, background: sm.bg }}>{sm.label}</span>
        {task.assignee && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 4, border: '1px solid #6b728044', color: '#dde4f8', background: '#141824' }}>{agentEmoji} {task.assignee}</span>}
        <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 4, border: `1px solid ${pm.color}44`, color: pm.color, background: '#141824' }}>{pm.label}</span>
        {task.escalated ? <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 4, border: '1px solid #ff527044', color: '#ff5270', background: '#200a10' }}>⚠️ 已升级</span> : null}
        {deps.length > 0 && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 4, border: '1px solid #f59e0b44', color: '#f59e0b', background: '#1a1508' }}>🔗 依赖{deps.length}</span>}
      </div>

      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, fontSize: 11, color: 'var(--muted)', marginBottom: 8 }}>
        <span>⏱ {timeAgo(task.updated_at)}</span>
        {(task.outputs_count || 0) > 0 && <span>📦 {task.outputs_count}产出</span>}
        {(task.comments_count || 0) > 0 && <span>💬 {task.comments_count}评论</span>}
        {rvm.icon && <span style={{ color: rvm.color }}>{rvm.icon}</span>}
      </div>

      {task.latest_event_detail && (
        <div style={{ fontSize: 10, color: '#8899aa', lineHeight: 1.4, padding: '3px 6px', background: '#1a1f2e', borderRadius: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginBottom: 8 }}>
          💬 {task.latest_event_detail}
        </div>
      )}

      {/* v2.7 子 Task 进度指示器 */}
      {subtaskCount > 0 && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8, padding: '4px 8px', background: '#1a1f2e', borderRadius: 6 }}>
          <span style={{ fontSize: 10, fontFamily: 'monospace', color: '#6a9eff', letterSpacing: 1 }}>
            {Array.from({ length: subtaskCount }, (_, i) => i < subtaskDone ? '■' : '□').join('')}
          </span>
          <span style={{ fontSize: 10, color: 'var(--muted)' }}>{subtaskDone}/{subtaskCount}</span>
          {activeStage && <span style={{ fontSize: 10, color: '#f59e0b' }}>· {activeStage}</span>}
        </div>
      )}

      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderTop: '1px solid var(--line)', paddingTop: 8 }}>
        <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
          <span style={{ width: 8, height: 8, borderRadius: 4, display: 'inline-block', background: STATUS_DOT_COLOR[task.status] || '#6b7280' }} />
          {task.retry_count > 0 && <span style={{ fontSize: 10, color: '#f59e0b' }}>🔄 x{task.retry_count}</span>}
          {task.risk_level && task.risk_level !== 'standard' && <span style={{ fontSize: 10, padding: '1px 5px', borderRadius: 3, background: `${rm.color}22`, color: rm.color }}>⚠️ {rm.label}</span>}
        </div>
        <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
          {task.deadline && (
            <span style={{ fontSize: 10, color: new Date(task.deadline).getTime() - Date.now() < 3600000 ? '#ff5270' : 'var(--muted)' }}>
              📅 {formatDeadline(task.deadline)}
            </span>
          )}
        </div>
      </div>
    </div>
  );
}

// ── 卡片快捷按钮（每状态最多3个） ──
// 状态指示圆点颜色
const STATUS_DOT_COLOR: Record<string, string> = {
  pending: '#f5c842', claimed: '#f5c842',
  working: '#2ecc8a', review: '#6a9eff',
  waiting_human: '#f59e0b', escalated: '#ff5270',
  failed: '#ff5270', blocked: '#ff5270',
  paused: '#6b7280', done: '#6b7280', cancelled: '#6b7280',
};

function EmptyState({ hasProject }: { hasProject: boolean }) {
  return (
    <div style={{ textAlign: 'center', padding: 60, color: 'var(--muted)' }}>
      <div style={{ fontSize: 48, marginBottom: 16 }}>{hasProject ? '📭' : '📂'}</div>
      <div style={{ fontSize: 16, fontWeight: 600, marginBottom: 8 }}>
        {hasProject ? '暂无任务' : '请先创建项目'}
      </div>
      <div style={{ fontSize: 12 }}>
        {hasProject
          ? '点击右上角「新建军令」创建第一个任务'
          : '在「系统设置」中创建项目，或通过 API 创建'}
      </div>
    </div>
  );
}

// ── 加载骨架 ──
function LoadingSkeleton() {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))', gap: 12 }}>
      {[1, 2, 3].map(i => (
        <div key={i} style={{ background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 14, padding: 18, opacity: 0.5 }}>
          <div style={{ height: 20, background: 'var(--panel2)', borderRadius: 4, marginBottom: 12, width: '60%' }} />
          <div style={{ height: 14, background: 'var(--panel2)', borderRadius: 4, marginBottom: 8, width: '80%' }} />
          <div style={{ height: 14, background: 'var(--panel2)', borderRadius: 4, width: '40%' }} />
        </div>
      ))}
    </div>
  );
}

// ── 主组件 ──
export default function EdictBoard() {
  const setModalTaskId = useStore(s => s.setModalTaskId);
  const v2tasks = useStore(s => s.v2tasks);
  const v2tasksLoading = useStore(s => s.v2tasksLoading);
  const selectedProjectId = useStore(s => s.selectedProjectId);
  const setSelectedProjectId = useStore(s => s.setSelectedProjectId);
  const projects = useStore(s => s.projects);
  const loadV2Tasks = useStore(s => s.loadV2Tasks);
  const loadLive = useStore(s => s.loadLive);
  const loadProjects = useStore(s => s.loadProjects);
  const toast = useStore(s => s.toast);
  const loadV2TaskDetail = useStore(s => s.loadV2TaskDetail);
  const [statusFilter, setStatusFilter] = useState<string>('all');
  const [searchQuery, setSearchQuery] = useState('');
  const [archiveFilter, setArchiveFilter] = useState<'active' | 'archived' | 'all'>('active');
  const [showProjectAction, setShowProjectAction] = useState(false);
  const [showNewProject, setShowNewProject] = useState(false);
  const [showProjectConfirm, setShowProjectConfirm] = useState<{id: string, name: string, action: 'archive' | 'delete'} | null>(null);

  // 初始化加载 + 轮询（空字符串 = 全部任务模式，也需要加载）
  useEffect(() => {
    loadV2Tasks();
  }, [selectedProjectId]);

  // ── 卡片动作处理 ──
  const handleCardAction = async (taskId: string, action: string, taskPid?: string) => {
    const pid = taskPid || (selectedProjectId && !selectedProjectId.startsWith('__') ? selectedProjectId : null);
    if (!pid) { toast('请先选择具体项目', 'err'); return; }
    try {
      if (action === 'archive') {
        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 if (action === 'approve') {
        const res = await fetch(`/api/projects/${pid}/tasks/${taskId}/status`, {
          method: 'POST', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ status: 'done' }),
        });
        if (res.ok) { toast(`✅ 已通过`); loadV2Tasks(); }
        else toast('操作失败', 'err');
      } else {
        const res = await fetch(`/api/projects/${pid}/tasks/${taskId}/status`, {
          method: 'POST', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ status: action }),
        });
        if (res.ok) { toast(`✅ 状态已更新`); loadV2Tasks(); }
        else toast('操作失败', 'err');
      }
    } catch { toast('网络错误', 'err'); }
  };

  // 项目操作处理
  const handleProjectAction = async (action: 'archive' | 'delete', projectId: string) => {
    setShowProjectAction(false);
    const projName = projects[projectId]?.name || projectId;
    setShowProjectConfirm({ id: projectId, name: projName, action });
  };

  const confirmProjectAction = async () => {
    if (!showProjectConfirm) return;
    const { id, action } = showProjectConfirm;
    try {
      let ok = false;
      if (action === 'archive') {
        const res = await fetch(`/api/projects/${id}/archive`, { method: 'POST' });
        ok = res.ok;
      } else {
        const res = await fetch(`/api/projects/${id}`, { method: 'DELETE' });
        ok = res.ok;
      }
      if (ok) {
        toast(action === 'archive' ? `📦 已归档 ${id}` : `🗑️ 已删除 ${id}`);
        setSelectedProjectId('');
        loadLive();
      } else {
        toast('操作失败', 'err');
      }
    } catch { toast('网络错误', 'err'); }
    setShowProjectConfirm(null);
  };

  // 新建项目
  const handleCreateProject = async (id: string, name: string) => {
    try {
      const res = await fetch('/api/projects', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ id, name }),
      });
      if (res.ok) { toast(`✅ 已创建 ${name}`); loadLive(); setSelectedProjectId(id); }
      else { const d = await res.json(); toast(d.detail || '创建失败', 'err'); }
    } catch { toast('网络错误', 'err'); }
    setShowNewProject(false);
  };

  const tasks = v2tasks as V2Task[];

  // 第 1 行筛选器：全部/活跃/归档
  const archiveFiltered = tasks.filter(t => {
    if (archiveFilter === 'active') return !t.archived;
    if (archiveFilter === 'archived') return !!t.archived;
    return true;
  });

  const filters = [
    { key: 'all', label: '全部', icon: '📋' },
    { key: 'pending', label: '待认领', icon: '📋' },
    { key: 'claimed', label: '已认领', icon: '👤' },
    { key: 'working', label: '执行中', icon: '⚔️' },
    { key: 'review', label: '审查中', icon: '🔍' },
    { key: 'paused', label: '已暂停', icon: '⏸' },
    { key: 'escalated', label: '已升级', icon: '⚠️' },
    { key: 'waiting_human', label: '等人工', icon: '🔔' },
    { key: 'done', label: '已完成', icon: '✅' },
    { key: 'failed', label: '失败', icon: '❌' },
    { key: 'blocked', label: '阻塞', icon: '🚧' },
    { key: 'cancelled', label: '已取消', icon: '🚫' },
  ];

  // 使用 archiveFiltered 代替 tasks 来构建子 Task 索引和筛选
  const subtaskIndex: Record<string, { total: number; done: number; activeStage: string | null }> = {};
  archiveFiltered.forEach(t => {
    if (t.parent_task) {
      if (!subtaskIndex[t.parent_task]) subtaskIndex[t.parent_task] = { total: 0, done: 0, activeStage: null };
      subtaskIndex[t.parent_task].total++;
      if (t.status === 'done') subtaskIndex[t.parent_task].done++;
      if (['working', 'claimed', 'review', 'paused', 'escalated', 'waiting_human'].includes(t.status) && !subtaskIndex[t.parent_task].activeStage) {
        subtaskIndex[t.parent_task].activeStage = t.stage || t.status;
      }
    }
  });

  let filtered = archiveFiltered.filter(t => !t.parent_task); // 只显示顶层 Task
  if (statusFilter !== 'all') filtered = filtered.filter(t => t.status === statusFilter);
  if (searchQuery.trim()) {
    const q = searchQuery.trim().toLowerCase();
    filtered = filtered.filter(t => (t.title || '').toLowerCase().includes(q) || (t.id || '').toLowerCase().includes(q));
  }

  const order: Record<string, number> = { escalated: 0, waiting_human: 1, working: 2, review: 3, claimed: 4, paused: 5, blocked: 6, pending: 7, failed: 8, done: 9, cancelled: 10 };
  filtered.sort((a, b) => (order[a.status] ?? 11) - (order[b.status] ?? 11));

  const topLevelTasks = archiveFiltered.filter(t => !t.parent_task);
  const counts: Record<string, number> = { all: topLevelTasks.length };
  topLevelTasks.forEach(t => { counts[t.status] = (counts[t.status] || 0) + 1; });

  // 统计始终基于未归档的非终态任务（不受 archiveFilter 影响）
  const activeTasks = topLevelTasks.filter(t => !t.archived && !['done','cancelled'].includes(t.status));
  const activeCount = activeTasks.length;
  const doneCount = topLevelTasks.filter(t => !t.archived && t.status === 'done').length;
  const failedCount = topLevelTasks.filter(t => !t.archived && ['failed', 'blocked'].includes(t.status)).length;
  const reviewCount = topLevelTasks.filter(t => !t.archived && t.status === 'review').length;
  const archivedCount = topLevelTasks.filter(t => t.archived).length;

  // 下拉菜单数字：从 v2tasks 聚合（单一数据源，与看板统计一致）
  const topLevelAll = (v2tasks as any[]).filter(t => !t.parent_task);
  const countByProjectAndFilter = (pid: string) => {
    return topLevelAll.filter(t => {
      if (t._projectId !== pid) return false;
      if (archiveFilter === 'archived') return !!t.archived;
      if (archiveFilter === 'active') return !t.archived;
      return true;
    }).length;
  };
  const countAllByFilter = () => {
    return topLevelAll.filter(t => {
      if (archiveFilter === 'archived') return !!t.archived;
      if (archiveFilter === 'active') return !t.archived;
      return true;
    }).length;
  };

  if (v2tasksLoading && tasks.length === 0) return <LoadingSkeleton />;

  return (
    <div>
      {/* 顶部统计 */}
      <div style={{ display: 'flex', gap: 10, marginBottom: 14 }}>
        <div style={{ flex: 1, padding: '10px 14px', background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 10 }}>
          <div style={{ fontSize: 10, color: 'var(--muted)', marginBottom: 2 }}>活跃任务</div>
          <div style={{ fontSize: 22, fontWeight: 700, color: '#6a9eff' }}>{activeCount}</div>
        </div>
        <div style={{ flex: 1, padding: '10px 14px', background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 10 }}>
          <div style={{ fontSize: 10, color: 'var(--muted)', marginBottom: 2 }}>已完成</div>
          <div style={{ fontSize: 22, fontWeight: 700, color: '#2ecc8a' }}>{doneCount}</div>
        </div>
        <div style={{ flex: 1, padding: '10px 14px', background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 10 }}>
          <div style={{ fontSize: 10, color: 'var(--muted)', marginBottom: 2 }}>失败/阻塞</div>
          <div style={{ fontSize: 22, fontWeight: 700, color: '#ff5270' }}>{failedCount}</div>
        </div>
        <div style={{ flex: 1, padding: '10px 14px', background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 10 }}>
          <div style={{ fontSize: 10, color: 'var(--muted)', marginBottom: 2 }}>审查中</div>
          <div style={{ fontSize: 22, fontWeight: 700, color: '#818cf8' }}>{reviewCount}</div>
        </div>
      </div>

      {/* 第 1 行：项目切换 + 归档控制 */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8, position: 'relative' }}>
        <select
          value={selectedProjectId || ''}
          onChange={(e) => setSelectedProjectId(e.target.value || '')}
          style={{
            fontSize: 11, padding: '3px 8px', borderRadius: 6,
            border: '1px solid var(--line)', background: 'var(--panel)',
            color: 'var(--fg)', cursor: 'pointer', minWidth: 120,
          }}
        >
          <option value="">⮜ 全部任务 ({countAllByFilter()})</option>
          <option value="_general">📝 一般任务 ({countByProjectAndFilter('_general')})</option>
          <option disabled style={{ color: '#555' }}>── 正式项目 ──</option>
          {Object.entries(projects).filter(([pid]) => !pid.startsWith('_') && pid !== '__general__').map(([pid, info]: [string, any]) => (
            <option key={pid} value={pid}>📁 {info.name || pid} ({countByProjectAndFilter(pid)})</option>
          ))}
        </select>
        {selectedProjectId && !selectedProjectId.startsWith('_') && selectedProjectId !== '__general__' && (() => {
          const pInfo = projects[selectedProjectId] as any;
          const isLocked = pInfo?.source === 'sanguo_projects_scan' || selectedProjectId === '_general';
          return isLocked ? (
            <button disabled title="自动发现的项目不可删除" style={{
              padding: '2px 6px', borderRadius: 4, fontSize: 11,
              border: '1px solid var(--line)', background: 'var(--panel)',
              color: '#555', cursor: 'not-allowed', opacity: 0.4,
            }}>🔒</button>
          ) : (
            <button
              onClick={() => setShowProjectAction(!showProjectAction)}
              title="项目操作"
              style={{
                padding: '2px 6px', borderRadius: 4, fontSize: 11,
                border: '1px solid var(--line)', background: 'var(--panel)',
                color: 'var(--fg)', cursor: 'pointer',
              }}
            >⋯</button>
          );
        })()}
        {showProjectAction && selectedProjectId && !selectedProjectId.startsWith('_') && (
          <ProjectActionPopup
            projectId={selectedProjectId}
            projectName={projects[selectedProjectId]?.name || selectedProjectId}
            onAction={handleProjectAction}
            onClose={() => setShowProjectAction(false)}
          />
        )}
        <button
          onClick={() => setShowNewProject(true)}
          title="新建项目"
          style={{
            padding: '2px 8px', borderRadius: 4, fontSize: 11,
            border: '1px solid var(--line)', background: 'var(--panel)',
            color: 'var(--fg)', cursor: 'pointer',
          }}
        >＋新建项目</button>
        {(['active', 'archived', 'all'] as const).map(f => (
          <button key={f} onClick={() => setArchiveFilter(f)} style={{
            padding: '3px 10px', borderRadius: 6, fontSize: 11,
            border: `1px solid ${archiveFilter === f ? 'var(--acc)' : '#2a3550'}`,
            background: archiveFilter === f ? 'var(--acc)22' : '#161b2e',
            color: archiveFilter === f ? 'var(--acc)' : '#8899aa',
            cursor: 'pointer', transition: 'all .15s',
          }}>
            {f === 'active' ? '活跃' : f === 'archived' ? '归档' : '全部'}
          </button>
        ))}
        <span style={{ fontSize: 10, color: 'var(--muted)', marginLeft: 4 }}>活跃{activeCount} · 归档{archivedCount} · 共{topLevelTasks.length}</span>
        <button onClick={async () => {
          const pid = selectedProjectId && !selectedProjectId.startsWith('__') ? selectedProjectId : null;
          if (!pid) { toast('请选择具体项目后归档', 'err'); return; }
          try {
            const res = await fetch(`/api/projects/${pid}/tasks/archive-done`, { method: 'POST' });
            const r = await res.json();
            if (r.ok || r.count) { toast(`📦 已归档 ${r.count || 0} 个任务`); loadV2Tasks(); }
            else toast('归档失败', 'err');
          } catch { toast('归档失败', 'err'); }
        }} style={{
          marginLeft: 'auto', padding: '3px 10px', borderRadius: 6, fontSize: 11,
          border: '1px solid #6b728044', background: '#161b2e', color: '#6b7280',
          cursor: 'pointer', transition: 'all .15s',
        }}>📦 一键归档</button>
      </div>

      {/* 第 2 行：状态筛选 + 搜索 */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 4, padding: '0 0 6px', flexWrap: 'wrap' }}>
        {filters.map(f => (
          <button key={f.key} onClick={() => setStatusFilter(f.key)} style={{
            padding: '2px 8px', borderRadius: 5, fontSize: 10,
            border: `1px solid ${statusFilter === f.key ? 'var(--acc)' : '#2a3550'}`,
            background: statusFilter === f.key ? 'var(--acc)22' : '#161b2e',
            color: statusFilter === f.key ? 'var(--acc)' : '#8899aa',
            cursor: 'pointer', transition: 'all .15s',
            display: 'flex', alignItems: 'center', gap: 2,
          }}>
            <span>{f.icon}</span><span>{f.label}</span>
            {(counts[f.key] || 0) > 0 && (
              <span style={{ fontSize: 9, background: statusFilter === f.key ? 'var(--acc)' : '#2a3550', color: statusFilter === f.key ? '#000' : '#8899aa', borderRadius: 8, padding: '0 4px', minWidth: 14, textAlign: 'center' }}>{counts[f.key]}</span>
            )}
          </button>
        ))}
        <div style={{ marginLeft: 'auto', position: 'relative' }}>
          <input type="text" placeholder="搜索任务..." value={searchQuery} onChange={e => setSearchQuery(e.target.value)}
            style={{ padding: '4px 10px 4px 28px', borderRadius: 6, fontSize: 11, border: '1px solid #2a3550', background: '#161b2e', color: '#c0ccdd', width: 180, outline: 'none' }}
          />
          <span style={{ position: 'absolute', left: 8, top: '50%', transform: 'translateY(-50%)', fontSize: 12, color: '#556' }}>🔍</span>
        </div>
      </div>

      {/* 卡片网格 */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))', gap: 12, marginTop: 12 }}>
        {filtered.map(t => {
          const sub = subtaskIndex[t.id] || { total: 0, done: 0, activeStage: null };
          return <TaskCard key={t.id} task={t} subtaskCount={sub.total} subtaskDone={sub.done} activeStage={sub.activeStage} onOpen={() => setModalTaskId(t.id)} onAction={(a: string) => handleCardAction(t.id, a, (t as any)._projectId)} />;
        })}
        {filtered.length === 0 && tasks.length > 0 && (
          <div style={{ gridColumn: '1/-1', textAlign: 'center', padding: 40, color: 'var(--muted)' }}>当前筛选无匹配任务</div>
        )}
      </div>

      {archiveFiltered.length === 0 && <EmptyState hasProject={true} />}

      {/* 项目操作确认框 */}
      {showProjectConfirm && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }} onClick={() => setShowProjectConfirm(null)}>
          <div style={{ background: '#1a2236', border: '1px solid var(--line)', borderRadius: 12, padding: 24, minWidth: 320 }} onClick={e => e.stopPropagation()}>
            <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 12 }}>
              {showProjectConfirm.action === 'archive' ? '📦 归档项目' : '🗑️ 删除项目'}
            </div>
            <div style={{ fontSize: 13, color: 'var(--muted)', marginBottom: 16 }}>
              确定要{showProjectConfirm.action === 'archive' ? '归档' : '删除'}「{showProjectConfirm.name}」吗？
              {showProjectConfirm.action === 'delete' && '\n此操作为逻辑删除，不会物理删除数据。'}
            </div>
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
              <button onClick={() => setShowProjectConfirm(null)} style={{ padding: '6px 16px', borderRadius: 6, fontSize: 12, border: '1px solid var(--line)', background: 'var(--panel)', color: 'var(--fg)', cursor: 'pointer' }}>取消</button>
              <button onClick={confirmProjectAction} style={{ padding: '6px 16px', borderRadius: 6, fontSize: 12, border: 'none', background: showProjectConfirm.action === 'delete' ? '#ff4757' : 'var(--acc)', color: '#fff', cursor: 'pointer' }}>
                {showProjectConfirm.action === 'archive' ? '归档' : '删除'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* 新建项目对话框 */}
      {showNewProject && (
        <NewProjectDialog
          onSubmit={handleCreateProject}
          onClose={() => setShowNewProject(false)}
        />
      )}
    </div>
  );
}

// 项目操作弹出菜单
function ProjectActionPopup({ projectId, projectName, onAction, onClose }: {
  projectId: string; projectName: string;
  onAction: (action: 'archive' | 'delete', id: string) => void;
  onClose: () => void;
}) {
  return (
    <div style={{
      position: 'absolute', top: '100%', left: 0, zIndex: 100,
      background: '#1a2236', border: '1px solid var(--line)', borderRadius: 8,
      padding: 4, minWidth: 140, boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
    }}>
      <button onClick={() => onAction('archive', projectId)} style={{
        display: 'block', width: '100%', textAlign: 'left', padding: '6px 12px',
        fontSize: 12, border: 'none', background: 'transparent', color: 'var(--fg)',
        cursor: 'pointer', borderRadius: 4, transition: 'background 0.15s',
      }} onMouseEnter={e => (e.currentTarget.style.background = 'var(--panel2)')} onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}>📦 归档项目</button>
      <button onClick={() => onAction('delete', projectId)} style={{
        display: 'block', width: '100%', textAlign: 'left', padding: '6px 12px',
        fontSize: 12, border: 'none', background: 'transparent', color: '#ff4757',
        cursor: 'pointer', borderRadius: 4, transition: 'background 0.15s',
      }} onMouseEnter={e => (e.currentTarget.style.background = 'var(--panel2)')} onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}>🗑️ 删除项目</button>
    </div>
  );
}

// 新建项目对话框
function NewProjectDialog({ onSubmit, onClose }: {
  onSubmit: (id: string, name: string) => void;
  onClose: () => void;
}) {
  const [name, setName] = useState('');
  const handleSubmit = () => {
    if (!name.trim()) return;
    // ID 自动生成：project-{yyyyMMddHHmmss}
    const now = new Date();
    const id = `project-${now.getFullYear()}${String(now.getMonth()+1).padStart(2,'0')}${String(now.getDate()).padStart(2,'0')}${String(now.getHours()).padStart(2,'0')}${String(now.getMinutes()).padStart(2,'0')}${String(now.getSeconds()).padStart(2,'0')}`;
    onSubmit(id, name.trim());
  };
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }} onClick={onClose}>
      <div style={{ background: '#1a2236', border: '1px solid var(--line)', borderRadius: 12, padding: 24, minWidth: 360 }} onClick={e => e.stopPropagation()}>
        <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 16 }}>➕ 新建项目</div>
        <div style={{ marginBottom: 16 }}>
          <label style={{ fontSize: 11, color: 'var(--muted)', display: 'block', marginBottom: 4 }}>项目名称</label>
          <input value={name} onChange={e => setName(e.target.value)} placeholder="输入项目名称" style={{ width: '100%', padding: '6px 10px', borderRadius: 6, border: '1px solid var(--line)', background: 'var(--panel)', color: 'var(--fg)', fontSize: 12, boxSizing: 'border-box' }} />
        </div>
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onClose} style={{ padding: '6px 16px', borderRadius: 6, fontSize: 12, border: '1px solid var(--line)', background: 'var(--panel)', color: 'var(--fg)', cursor: 'pointer' }}>取消</button>
          <button disabled={!name.trim()} onClick={handleSubmit} style={{ padding: '6px 16px', borderRadius: 6, fontSize: 12, border: 'none', background: 'var(--acc)', color: '#fff', cursor: name.trim() ? 'pointer' : 'not-allowed', opacity: name.trim() ? 1 : 0.5 }}>创建</button>
        </div>
      </div>
    </div>
  );
}
