// 飞轮健康面板（跨项目）：版本队列对比 / 阻塞原因 / 停转与休眠归档 / Git 执行质量 / 梦核分析
// 2026-07-02 ui-restructure N3：从 BlueprintModal 侧板迁出为洞察页一级内容（跨项目内容不属于单项目弹窗）
import { useState, useMemo } from "react";
import { useQuery } from "@tanstack/react-query";
import {
  getProjects,
  getFlywheelStats,
  generateMuhePrompt,
  archiveProjectDocs,
  ingestFullGitHistory,
  getAllCommitHealth,
  type Project,
  type FlywheelStats,
  type StalledProject,
  type CommitHealthWithName,
} from "../../lib/commands";

function StalledProjectRow({ p, projectsMap, onArchive }: {
  p: StalledProject;
  projectsMap: Record<string, string>; // name → path
  onArchive?: (path: string, name: string) => void;
}) {
  const path = projectsMap[p.name];
  return (
    <div className="flex items-center justify-between gap-2">
      <div className="min-w-0">
        <p className="text-[11px] text-amber-700 truncate">· {p.name}</p>
        <p className="text-[10px] text-amber-400">最后活跃: {p.last_active}</p>
      </div>
      {onArchive && path && (
        <button
          onClick={() => onArchive(path, p.name)}
          className="text-[10px] text-amber-600 bg-amber-100 hover:bg-amber-200 px-1.5 py-0.5 rounded shrink-0 transition-colors"
        >
          归档
        </button>
      )}
    </div>
  );
}

function MetricRow({ label, value, good, invert = false }: {
  label: string;
  value: string;
  good: boolean;
  invert?: boolean;
}) {
  const isGood = invert ? !good : good;
  return (
    <div className="flex items-center justify-between">
      <span className="text-[10px] text-gray-400">{label}</span>
      <span className={`text-[10px] font-mono font-semibold ${isGood ? "text-green-600" : "text-yellow-600"}`}>
        {value}
      </span>
    </div>
  );
}

export function FlywheelPanel() {
  const { data: projects } = useQuery<Project[]>({
    queryKey: ["projects-for-flywheel"],
    queryFn: () => getProjects(),
  });

  const { projectPaths, projectsMap, projectsWithPath } = useMemo(() => {
    const paths = (projects ?? [])
      .map((p) => p.win_path || p.wsl_path || "")
      .filter(Boolean);
    const map: Record<string, string> = {};
    const withPath: Array<{ id: string; name: string; path: string }> = [];
    for (const p of (projects ?? [])) {
      const path = p.win_path || p.wsl_path || "";
      if (!path) continue;
      withPath.push({ id: p.id, name: p.name, path });
      map[p.name] = path;
      const dirName = path.replace(/\\/g, "/").split("/").filter(Boolean).pop() ?? "";
      if (dirName && !map[dirName]) map[dirName] = path;
    }
    return { projectPaths: paths, projectsMap: map, projectsWithPath: withPath };
  }, [projects]);

  const { data: commitHealth, refetch: refetchHealth } = useQuery<CommitHealthWithName[]>({
    queryKey: ["commit-health-all"],
    queryFn: () => getAllCommitHealth(),
  });

  const [ingesting, setIngesting] = useState(false);
  const [ingestSummary, setIngestSummary] = useState<string | null>(null);

  const { data: stats, isLoading } = useQuery<FlywheelStats>({
    queryKey: ["flywheel-stats", projectPaths],
    queryFn: () => getFlywheelStats(projectPaths),
    enabled: projectPaths.length > 0,
  });

  const [muhePrompt, setMuhePrompt] = useState("");
  const [generating, setGenerating] = useState(false);
  const [copied, setCopied] = useState(false);
  const [archiveMsg, setArchiveMsg] = useState<string | null>(null);
  const [archiving, setArchiving] = useState<string | null>(null);
  const [pendingArchive, setPendingArchive] = useState<{ path: string; name: string } | null>(null);

  const handleIngestAll = async () => {
    setIngesting(true);
    setIngestSummary(null);
    let totalImported = 0;
    let totalSkipped = 0;
    for (const { id, path } of projectsWithPath) {
      try {
        const r = await ingestFullGitHistory(id, path);
        totalImported += r.imported;
        totalSkipped += r.skipped;
      } catch {
        // 单个项目失败不中断其他项目
      }
    }
    await refetchHealth();
    setIngestSummary(`新导入 ${totalImported} 条，跳过 ${totalSkipped} 条`);
    setIngesting(false);
  };

  const handleGenerateMuhe = async () => {
    if (!stats) return;
    setGenerating(true);
    try {
      const result = await generateMuhePrompt(stats);
      setMuhePrompt(result);
    } finally {
      setGenerating(false);
    }
  };

  const handleCopy = () => {
    navigator.clipboard.writeText(muhePrompt).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };

  const handleArchiveConfirm = async () => {
    if (!pendingArchive) return;
    const { path, name } = pendingArchive;
    setPendingArchive(null);
    setArchiving(name);
    setArchiveMsg(null);
    try {
      const result = await archiveProjectDocs(path);
      setArchiveMsg(`✓ 已归档 ${result.archived_files.length} 个文件 → ${result.archive_dir}`);
    } catch (e) {
      setArchiveMsg(`归档失败: ${String(e)}`);
    } finally {
      setArchiving(null);
    }
  };

  const fmtPct = (v: number) => `${(v * 100).toFixed(1)}%`;

  // 去重——90天休眠的项目不在60天停转里重复展示
  const stalledOnlyProjects = (stats?.stalled_projects ?? []).filter(
    (p) => !(stats?.ai_doc_stale_projects ?? []).some((q) => q.name === p.name)
  );

  return (
    <div className="p-4 space-y-4">
      <div>
        <h3 className="text-sm font-semibold text-gray-800">飞轮健康</h3>
        {stats && (
          <p className="text-[10px] text-gray-400 mt-0.5">
            {stats.projects_with_data}/{stats.total_projects_scanned} 个项目有数据
          </p>
        )}
      </div>

      {isLoading ? (
        <p className="text-xs text-gray-400 text-center py-6">聚合中…</p>
      ) : !stats || stats.projects_with_data === 0 ? (
        <div className="text-center py-6 space-y-2">
          <p className="text-xs text-gray-400">暂无飞轮数据</p>
          <p className="text-[11px] text-gray-300">打开各项目蓝图后数据将自动积累</p>
        </div>
      ) : (
        <>
          {/* CONVENTIONS 版本队列对比 */}
          {stats.cohorts.length > 0 && (
            <div className="bg-white rounded-lg border border-gray-100 p-3 space-y-2">
              <p className="text-[10px] font-semibold text-gray-400 uppercase tracking-wider">版本队列对比</p>
              <div className="space-y-3">
                {stats.cohorts.map((c) => (
                  <div key={c.conventions_version} className="space-y-1.5">
                    <div className="flex items-center justify-between">
                      <span className="text-xs font-mono text-gray-600">v{c.conventions_version}</span>
                      <span className="text-[10px] text-gray-400">{c.project_count} 个项目</span>
                    </div>
                    <div className="grid grid-cols-2 gap-x-3 gap-y-0.5">
                      <MetricRow label="可派发率" value={fmtPct(c.dispatchable_rate)} good={c.dispatchable_rate > 0.3} />
                      <MetricRow label="任务完成率" value={fmtPct(c.completion_rate)} good={c.completion_rate > 0.5} />
                      <MetricRow label="飞轮持续率" value={fmtPct(c.continuity_rate)} good={c.continuity_rate > 0.6} />
                      <MetricRow label="阻塞密度" value={fmtPct(c.blocked_density)} good={c.blocked_density < 0.1} invert />
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* TOP blocked reasons */}
          {stats.top_blocked_reasons.length > 0 && (
            <div className="bg-white rounded-lg border border-gray-100 p-3 space-y-2">
              <p className="text-[10px] font-semibold text-gray-400 uppercase tracking-wider">TOP 阻塞原因</p>
              <ul className="space-y-1">
                {stats.top_blocked_reasons.map((r, i) => (
                  <li key={i} className="flex items-start gap-2">
                    <span className="text-[10px] text-gray-400 shrink-0 mt-0.5">#{i + 1}</span>
                    <span className="text-[11px] text-red-600 flex-1">{r.reason}</span>
                    <span className="text-[10px] text-gray-400 shrink-0">×{r.count}</span>
                  </li>
                ))}
              </ul>
            </div>
          )}

          {/* 仅展示"停转但未到90天"的项目 */}
          {stalledOnlyProjects.length > 0 && (
            <div className="bg-amber-50 rounded-lg border border-amber-100 p-3 space-y-1.5">
              <p className="text-[10px] font-semibold text-amber-500 uppercase tracking-wider">
                飞轮停转（60–90天无快照）
              </p>
              {stalledOnlyProjects.map((p, i) => (
                <StalledProjectRow key={i} p={p} projectsMap={projectsMap} />
              ))}
            </div>
          )}

          {/* AI文档休眠项目（≥90天） */}
          {stats.ai_doc_stale_projects.length > 0 && (
            <div className="bg-red-50 rounded-lg border border-red-100 p-3 space-y-1.5">
              <p className="text-[10px] font-semibold text-red-500 uppercase tracking-wider">
                AI文档休眠（90天+无快照）
              </p>
              <p className="text-[10px] text-red-400 mb-1">
                建议归档 .blueprint/modules/，清空 Claude 上下文负担。
              </p>
              {pendingArchive ? (
                <div className="bg-red-100 border border-red-200 rounded p-2.5 space-y-2">
                  <p className="text-[11px] text-red-700 font-semibold">
                    确认归档「{pendingArchive.name}」？
                  </p>
                  <p className="text-[10px] text-red-500 leading-relaxed">
                    将删除 .blueprint/modules/*.md，文件会备份到 .blueprint/archive/，但操作本身不可一键撤销。
                  </p>
                  <div className="flex gap-2">
                    <button
                      onClick={handleArchiveConfirm}
                      disabled={!!archiving}
                      className="flex-1 py-1 rounded bg-red-600 text-white text-[11px] hover:bg-red-700 disabled:opacity-50 transition-colors"
                    >
                      确认归档
                    </button>
                    <button
                      onClick={() => setPendingArchive(null)}
                      className="flex-1 py-1 rounded border border-red-200 text-red-500 text-[11px] hover:bg-red-50 transition-colors"
                    >
                      取消
                    </button>
                  </div>
                </div>
              ) : (
                stats.ai_doc_stale_projects.map((p, i) => (
                  <StalledProjectRow
                    key={i}
                    p={p}
                    projectsMap={projectsMap}
                    onArchive={archiving ? undefined : (path, name) => setPendingArchive({ path, name })}
                  />
                ))
              )}
              {archiving && (
                <p className="text-[10px] text-red-400">归档 {archiving} 中…</p>
              )}
              {archiveMsg && (
                <p className={`text-[10px] break-all ${archiveMsg.startsWith("✓") ? "text-green-600" : "text-red-600"}`}>
                  {archiveMsg}
                </p>
              )}
            </div>
          )}

          {/* 迭代跳变项目 */}
          {(stats.iteration_jumps ?? []).length > 0 && (
            <div className="bg-purple-50 rounded-lg border border-purple-100 p-3 space-y-1.5">
              <p className="text-[10px] font-semibold text-purple-500 uppercase tracking-wider">
                迭代跳变（主动重构，完成率重置）
              </p>
              {(stats.iteration_jumps ?? []).map((j, i) => (
                <div key={i} className="flex items-center gap-2 text-[11px]">
                  <span className="text-gray-600 truncate flex-1">{j.project}</span>
                  <span className="text-purple-400 shrink-0">迭代 {j.from_iteration}→{j.to_iteration}</span>
                  <span className={`shrink-0 font-mono ${j.rate_after < j.rate_before - 0.3 ? "text-amber-500 font-semibold" : "text-gray-400"}`}>
                    {(j.rate_before * 100).toFixed(0)}%→{(j.rate_after * 100).toFixed(0)}%
                    {j.rate_after < j.rate_before - 0.3 && " ⚠️"}
                  </span>
                </div>
              ))}
            </div>
          )}

          {/* 停滞模块 */}
          {(stats.stalled_modules ?? []).length > 0 && (
            <div className="bg-gray-50 rounded-lg border border-gray-100 p-3 space-y-1.5">
              <p className="text-[10px] font-semibold text-gray-400 uppercase tracking-wider">
                停滞模块（in_progress 但无可推进任务）
              </p>
              <ul className="space-y-0.5">
                {(stats.stalled_modules ?? []).map((m, i) => (
                  <li key={i} className="text-[11px] text-gray-500 font-mono">{m}</li>
                ))}
              </ul>
            </div>
          )}

          {/* Git 执行质量 */}
          <div className="bg-white rounded-lg border border-gray-100 p-3 space-y-2">
            <div className="flex items-center justify-between">
              <p className="text-[10px] font-semibold text-gray-400 uppercase tracking-wider">Git 执行质量</p>
              <button
                onClick={handleIngestAll}
                disabled={ingesting || projectsWithPath.length === 0}
                className="text-[10px] px-2 py-0.5 rounded bg-indigo-50 text-indigo-600 border border-indigo-200 hover:bg-indigo-100 disabled:opacity-40 transition-colors"
              >
                {ingesting ? "导入中…" : "全量导入"}
              </button>
            </div>
            {ingestSummary && (
              <p className="text-[10px] text-green-600">{ingestSummary}</p>
            )}
            {commitHealth && commitHealth.length > 0 ? (
              <div className="overflow-x-auto">
                <table className="w-full text-[10px]">
                  <thead>
                    <tr className="text-gray-400 border-b border-gray-100">
                      <th className="text-left pb-1 font-medium">项目</th>
                      <th className="text-right pb-1 font-medium">commits</th>
                      <th className="text-right pb-1 font-medium">规范化</th>
                      <th className="text-right pb-1 font-medium">返工率</th>
                      <th className="text-left pb-1 font-medium pl-2">TOP scope</th>
                    </tr>
                  </thead>
                  <tbody className="divide-y divide-gray-50">
                    {commitHealth.map((h) => {
                      return (
                        <tr key={h.project_id} className="hover:bg-gray-50">
                          <td className="py-1 text-gray-700 truncate max-w-[80px]" title={h.project_name}>
                            {h.project_name}
                          </td>
                          <td className="py-1 text-right font-mono text-gray-500">{h.total_commits}</td>
                          <td className={`py-1 text-right font-mono font-semibold ${h.conventional_rate >= 0.8 ? "text-green-600" : h.conventional_rate >= 0.5 ? "text-amber-500" : "text-red-500"}`}>
                            {Math.round(h.conventional_rate * 100)}%
                          </td>
                          <td className={`py-1 text-right font-mono ${h.rework_rate > 0.2 ? "text-red-500" : h.rework_rate > 0.1 ? "text-amber-500" : "text-gray-500"}`}>
                            {Math.round(h.rework_rate * 100)}%
                          </td>
                          <td className="py-1 pl-2 text-gray-400 truncate max-w-[100px]">
                            {h.top_scopes.slice(0, 2).map((s) => `${s.scope}(${s.commit_count})`).join(" ")}
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            ) : (
              <p className="text-[10px] text-gray-400 italic">
                暂无数据——点"全量导入"从各项目 git 历史初始化
              </p>
            )}
          </div>

          {/* 梦核分析 */}
          <div className="bg-white rounded-lg border border-gray-100 p-3 space-y-2">
            <p className="text-[10px] font-semibold text-gray-400 uppercase tracking-wider">梦核分析</p>
            <p className="text-[11px] text-gray-500 leading-relaxed">
              生成包含以上数据的提示词，交给 Claude Opus 做固化/遗忘判断。
            </p>
            {!muhePrompt ? (
              <button
                onClick={handleGenerateMuhe}
                disabled={generating}
                className="w-full py-1.5 rounded-lg bg-emerald-600 text-white text-xs hover:bg-emerald-700 disabled:opacity-50 transition-colors"
              >
                {generating ? "生成中…" : "生成梦核提示词"}
              </button>
            ) : (
              <div className="space-y-2">
                <textarea
                  readOnly
                  value={muhePrompt}
                  rows={10}
                  className="w-full font-mono text-[11px] text-gray-700 bg-gray-50 border border-gray-200 rounded-lg p-2.5 resize-none outline-none"
                  onClick={(e) => (e.target as HTMLTextAreaElement).select()}
                />
                <button
                  onClick={handleCopy}
                  className="w-full py-1.5 rounded-lg bg-emerald-600 text-white text-xs hover:bg-emerald-700 transition-colors"
                >
                  {copied ? "已复制 ✓" : "复制提示词"}
                </button>
                <button
                  onClick={() => setMuhePrompt("")}
                  className="w-full py-1 rounded-lg text-xs text-gray-400 hover:text-gray-600 transition-colors"
                >
                  重新生成
                </button>
                <p className="text-[10px] text-gray-400 text-center leading-relaxed">
                  复制 → 粘贴给 Claude Opus → 建议可在「全局治理」tab 直接应用（写入并同步所有项目）
                </p>
              </div>
            )}
          </div>
        </>
      )}
    </div>
  );
}
