import {
  CheckCircleFilled,
  CloseOutlined,
  UploadOutlined,
} from "@ant-design/icons";
import { useEffect, useRef, useState, type ChangeEvent, type DragEvent, type KeyboardEvent } from "react";
import "../../styles/pages/script-tokens-v5.css";
import "../../styles/pages/script-tokens.css";
import { evaluateScript } from "../../api/scriptEvalClient";
import { buildApiUrl, getStoredToken } from "../../api/serverConnection";
import { ShellIcon } from "../../components/ShellIcon";
import { useSessionStore } from "../../stores";

interface ScoreDimension {
  key: string;
  label: string;
  maxScore: number;
  hint: string;
  detail: string;
}

interface EvalResult {
  totalScore: number;
  grade: string;
  dimensionScores: Record<string, number>;
  subScores?: Record<string, Record<string, number>>;
  evidence?: Record<string, string[]>;
  summary: string;
  issues: string[];
  highlights: string[];
  suggestions: string[];
}

interface HistoryEntry {
  name: string;
  date: string;
  timestamp: number;
  score: number;
  grade: string;
  script?: string;
  result?: EvalResult;
}

function getGrade(score: number): string {
  if (score >= 97) return "S+";
  if (score >= 93) return "S";
  if (score >= 88) return "A+";
  if (score >= 83) return "A";
  if (score >= 78) return "B+";
  if (score >= 70) return "B";
  return "C";
}

const HISTORY_KEY = "omniai:script-eval-history";
const TEXT_FILE_EXTENSIONS = [
  ".txt",
  ".text",
  ".md",
  ".markdown",
  ".fountain",
  ".fdx",
  ".rtf",
  ".docx",
  ".doc",
  ".csv",
  ".tsv",
  ".json",
  ".jsonl",
  ".xml",
  ".html",
  ".htm",
  ".yaml",
  ".yml",
  ".toml",
  ".ini",
  ".conf",
  ".cfg",
  ".properties",
  ".log",
  ".srt",
  ".ass",
  ".ssa",
  ".vtt",
  ".sql",
  ".js",
  ".jsx",
  ".ts",
  ".tsx",
  ".py",
  ".java",
  ".c",
  ".cpp",
  ".h",
  ".hpp",
  ".cs",
  ".go",
  ".rs",
  ".php",
  ".rb",
  ".sh",
  ".bat",
  ".ps1",
  ".lua",
  ".swift",
  ".kt",
  ".kts",
] as const;
const TEXT_FILE_EXTENSION_SET = new Set<string>(TEXT_FILE_EXTENSIONS);
const TEXT_FILE_ACCEPT = TEXT_FILE_EXTENSIONS.join(",");
const TEXT_FILE_HINT = "支持常见文本格式：TXT / MD / DOCX / Fountain / FDX / RTF / JSON / CSV / XML / HTML / YAML / LOG / 字幕等";

function loadHistory(): HistoryEntry[] {
  try {
    const raw = localStorage.getItem(HISTORY_KEY);
    return raw ? (JSON.parse(raw) as HistoryEntry[]).sort((a, b) => b.timestamp - a.timestamp) : [];
  } catch { return []; }
}

function saveHistory(entries: HistoryEntry[]) {
  try { localStorage.setItem(HISTORY_KEY, JSON.stringify(entries.sort((a, b) => b.timestamp - a.timestamp).slice(0, 20))); } catch { /* quota exceeded */ }
}

function getFileExtension(filename: string): string {
  const dotIndex = filename.lastIndexOf(".");
  return dotIndex >= 0 ? filename.slice(dotIndex).toLowerCase() : "";
}

function isReadableTextFile(file: File, ext: string): boolean {
  const mime = file.type.toLowerCase();
  return (
    TEXT_FILE_EXTENSION_SET.has(ext) ||
    mime.startsWith("text/") ||
    mime === "application/json" ||
    mime === "application/xml" ||
    mime === "application/xhtml+xml" ||
    mime === "application/x-yaml" ||
    mime === "application/yaml"
  );
}

async function decodeTextFile(file: File): Promise<string> {
  const bytes = await file.arrayBuffer();
  const utf8 = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
  if (!utf8.includes("\uFFFD")) return utf8;

  try {
    return new TextDecoder("gb18030", { fatal: false }).decode(bytes);
  } catch {
    return utf8;
  }
}

function normalizeUploadedText(raw: string, ext: string): string {
  if (ext === ".rtf") {
    const text = raw
      .replace(/\\par[d]?/gi, "\n")
      .replace(/\\line/gi, "\n")
      .replace(/\\'[0-9a-f]{2}/gi, "")
      .replace(/\\[a-z]+\d* ?/gi, "")
      .replace(/[{}]/g, "")
      .replace(/\n{3,}/g, "\n\n")
      .trim();
    return text || raw;
  }

  if ([".html", ".htm", ".xml", ".fdx"].includes(ext) && typeof DOMParser !== "undefined") {
    try {
      const doc = new DOMParser().parseFromString(raw, ext === ".html" || ext === ".htm" ? "text/html" : "application/xml");
      const text = doc.documentElement.textContent?.replace(/\n{3,}/g, "\n\n").trim();
      return text || raw;
    } catch {
      return raw;
    }
  }

  return raw;
}


function formatFileSize(size: number): string {
  if (size < 1024) return `${size} B`;
  if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
  return `${(size / 1024 / 1024).toFixed(1)} MB`;
}

const SCORE_DIMENSIONS: ScoreDimension[] = [
  { key: "hook", label: "钩子设计", maxScore: 20, hint: "开篇吸引力·悬念设置·黄金三秒", detail: "开篇即抛出高概念钩子，悬念设置紧凑有力。" },
  { key: "character", label: "角色塑造", maxScore: 15, hint: "人物立体度·动机合理性·弧光设计", detail: "主角动机有铺垫，配角功能性较强，人物弧光尚可进一步深化。" },
  { key: "plot", label: "剧情结构", maxScore: 20, hint: "起承转合·节奏把控·冲突设计", detail: "起承转合完整，节奏把控稳健，冲突设计有张力。" },
  { key: "logic", label: "逻辑严密", maxScore: 15, hint: "世界观自洽·伏笔回收·因果链", detail: "世界观整体自洽，伏笔设置到位。" },
  { key: "visual", label: "场景构建", maxScore: 15, hint: "空间描写·视听语言·画面想象力", detail: "视觉意象统一而强烈，场景描写极具画面感。" },
  { key: "content", label: "内容深度", maxScore: 15, hint: "主题表达·情感共鸣·思想内核", detail: "核心设定将科技伦理与人性困境紧密结合，主题表达深刻有力。" },
];

const SUB_SCORE_LABELS: Record<string, string> = {
  openingImpact: "开篇冲击",
  suspenseChain: "悬念链",
  sceneHook: "场内钩子",
  structure: "结构完整",
  rhythm: "节奏推进",
  conflict: "冲突强度",
  reversal: "反转效率",
  motivation: "动机清晰",
  arc: "人物弧光",
  voice: "语言辨识",
  relationship: "关系张力",
  causality: "因果链",
  worldRules: "世界规则",
  foreshadowing: "伏笔回收",
  continuity: "连续性",
  sceneDetail: "场景细节",
  shotPotential: "镜头潜力",
  aigcFeasibility: "AIGC 可实现",
  theme: "主题表达",
  emotion: "情感共鸣",
  marketFit: "市场匹配",
  originality: "原创性",
};

function clampScore(score: unknown, maxScore: number): number {
  const numeric = Number(score);
  if (!Number.isFinite(numeric)) return 0;
  return Math.max(0, Math.min(maxScore, numeric));
}

function getDimensionScore(result: EvalResult, dim: ScoreDimension): number {
  const value = result.dimensionScores[dim.key] ?? (dim.key === "logic" ? result.dimensionScores.dialogue : undefined);
  return clampScore(value, dim.maxScore);
}

function formatSubScoreLabel(key: string): string {
  return SUB_SCORE_LABELS[key] ?? key.replace(/([A-Z])/g, " $1").trim();
}

function getDimensionSubScores(result: EvalResult, dim: ScoreDimension): Array<[string, number]> {
  const scores = result.subScores?.[dim.key] ?? (dim.key === "logic" ? result.subScores?.dialogue : undefined);
  if (!scores) return [];
  return Object.entries(scores)
    .map(([key, value]) => [key, clampScore(value, dim.maxScore)] as [string, number])
    .filter(([, value]) => value > 0)
    .slice(0, 5);
}

function normalizeEvidenceItems(evidence: unknown[] | undefined, limit: number): string[] {
  if (!Array.isArray(evidence)) return [];
  const items: string[] = [];
  for (const item of evidence) {
    const value = String(item).trim();
    if (!value) continue;
    items.push(value);
    if (items.length >= limit) break;
  }
  return items;
}

function getDimensionEvidence(result: EvalResult, dim: ScoreDimension): string[] {
  const evidence = result.evidence?.[dim.key] ?? (dim.key === "logic" ? result.evidence?.dialogue : undefined);
  return normalizeEvidenceItems(evidence, 3);
}

function formatReportMarkdown(result: EvalResult): string {
  const lines: string[] = [];
  lines.push(`# 剧本评测报告`);
  lines.push("");
  lines.push(`**综合评分**: ${result.totalScore} / 100 · ${result.grade} 级`);
  lines.push("");
  lines.push(`## 概要`);
  lines.push(result.summary);
  lines.push("");
  lines.push(`## 六维评分`);
  for (const dim of SCORE_DIMENSIONS) {
    const score = getDimensionScore(result, dim);
    const pct = Math.round((score / dim.maxScore) * 100);
    const subScores = getDimensionSubScores(result, dim);
    const evidence = getDimensionEvidence(result, dim);
    const nestedReportLines = [
      ...subScores.map(([key, value]) => `  - ${formatSubScoreLabel(key)}: ${value}`),
      ...evidence.map((item) => `  - 证据: ${item}`),
    ];
    lines.push(`- **${dim.label}**: ${score}/${dim.maxScore} (${pct}%) — ${dim.hint}`);
    lines.push(...nestedReportLines);
  }
  if (result.highlights.length > 0) {
    lines.push("");
    lines.push(`## 亮点`);
    result.highlights.forEach((h) => lines.push(`- ${h}`));
  }
  if (result.issues.length > 0) {
    lines.push("");
    lines.push(`## 扣分点`);
    result.issues.forEach((i) => lines.push(`- ${i}`));
  }
  if (result.suggestions.length > 0) {
    lines.push("");
    lines.push(`## 优化建议`);
    result.suggestions.forEach((s) => lines.push(`- ${s}`));
  }
  lines.push("");
  lines.push(`---`);
  lines.push(`*评测时间: ${new Date().toLocaleString("zh-CN")}*`);
  return lines.join("\n");
}

function ScriptTokensPage() {
  const [script, setScript] = useState("");
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState<EvalResult | null>(null);
  const [evalError, setEvalError] = useState<string | null>(null);
  const [uploadedFile, setUploadedFile] = useState<{ name: string; size: number } | null>(null);
  const [copied, setCopied] = useState(false);
  const [activeDim, setActiveDim] = useState<number | null>(null);
  const [animatedScore, setAnimatedScore] = useState(0);
  const [activeHistoryIndex, setActiveHistoryIndex] = useState<number>(0);
  const [history, setHistory] = useState<HistoryEntry[]>(loadHistory);
  const [isDragging, setIsDragging] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const scoreFrameRef = useRef<number | null>(null);

  const session = useSessionStore((s) => s.session);
  const hasContent = Boolean(script.trim());

  // Score animation
  useEffect(() => {
    if (!result) return;
    const start = performance.now();
    const target = result.totalScore;
    const dur = 1400;
    function tick(now: number) {
      const t = Math.min((now - start) / dur, 1);
      const e = 1 - Math.pow(1 - t, 3);
      setAnimatedScore(Math.round(e * target));
      if (t < 1) scoreFrameRef.current = requestAnimationFrame(tick);
    }
    scoreFrameRef.current = requestAnimationFrame(tick);
    return () => { if (scoreFrameRef.current) cancelAnimationFrame(scoreFrameRef.current); };
  }, [result]);

  const processUploadedFile = async (file: File) => {
    const ext = getFileExtension(file.name);
    const readable = isReadableTextFile(file, ext);
    setUploadedFile({ name: file.name, size: file.size });
    if (ext === ".docx" || ext === ".doc") {
      try {
        const formData = new FormData();
        formData.append("file", file);
        const token = getStoredToken();
        const resp = await fetch(buildApiUrl("files/extract-text"), {
          method: "POST",
          headers: token ? { Authorization: `Bearer ${token}` } : {},
          body: formData,
        });
        if (resp.ok) {
          const { text } = await resp.json();
          setScript(text || "");
        } else {
          const err = await resp.json().catch(() => ({ error: "解析失败" }));
          setScript(`[已上传文件：${file.name}]\n\n${err.error || "文件解析失败，请尝试另存为 TXT 格式后重新上传。"}`);
        }
      } catch {
        setScript(`[已上传文件：${file.name}]\n\n文件解析请求失败，请检查网络连接后重试。`);
      }
    } else if (readable) {
      const text = normalizeUploadedText(await decodeTextFile(file), ext);
      setScript(text);
    } else {
      setScript(`[已上传文件：${file.name}]\n\n暂不支持解析 ${ext ? ext.toUpperCase() : "未知"} 格式，请上传常见文本类文件。`);
    }
  };

  const handleFileUpload = async (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;
    await processUploadedFile(file);
    event.target.value = "";
  };

  const handleEvaluate = async () => {
    if (!hasContent) return;
    setLoading(true);
    setResult(null);
    setEvalError(null);
    setAnimatedScore(0);
    setActiveDim(null);
    try {
      const aiResult = await evaluateScript(script);
      setResult(aiResult);
      const g = getGrade(aiResult.totalScore);
      const entry: HistoryEntry = {
        name: uploadedFile?.name?.replace(/\.[^.]+$/, "") ?? `剧本 ${new Date().toLocaleDateString("zh-CN")}`,
        date: new Date().toLocaleDateString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }),
        timestamp: Date.now(),
        score: aiResult.totalScore,
        grade: g,
        script,
        result: aiResult,
      };
      const updated = [entry, ...loadHistory().filter((h) => h.name !== entry.name || h.score !== entry.score)].sort(
        (a, b) => b.timestamp - a.timestamp,
      );
      saveHistory(updated);
      setHistory(updated);
    } catch (err) {
      setEvalError(err instanceof Error ? err.message : "评测服务暂时不可用，请稍后重试");
    }
    setLoading(false);
  };

  const handleHistoryClick = (item: HistoryEntry, index: number) => {
    setActiveHistoryIndex(index);
    if (item.script) {
      setScript(item.script);
      setUploadedFile({ name: `${item.name}.txt`, size: item.script.length });
    }
    if (item.result) {
      setResult(item.result);
    } else {
      setResult(null);
    }
    setEvalError(null);
  };

  const handleReset = () => {
    setScript("");
    setResult(null);
    setEvalError(null);
    setUploadedFile(null);
    setCopied(false);
    setAnimatedScore(0);
    setActiveDim(null);
    if (fileInputRef.current) fileInputRef.current.value = "";
  };

  const handleCopyReport = async () => {
    if (!result) return;
    const text = formatReportMarkdown(result);
    try {
      await navigator.clipboard.writeText(text);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch {
      const ta = document.createElement("textarea");
      ta.value = text;
      ta.style.position = "fixed";
      ta.style.opacity = "0";
      document.body.appendChild(ta);
      ta.select();
      document.execCommand("copy");
      document.body.removeChild(ta);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }
  };

  const handleExportMarkdown = () => {
    if (!result) return;
    const md = formatReportMarkdown(result);
    const blob = new Blob([md], { type: "text/markdown;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `剧本评测_${result.grade}级_${result.totalScore}分_${new Date().toISOString().slice(0, 10)}.md`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const uploadKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    event.preventDefault();
    fileInputRef.current?.click();
  };

  const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    event.stopPropagation();
    if (event.dataTransfer.types.includes("Files")) {
      setIsDragging(true);
    }
  };

  const handleDragLeave = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    event.stopPropagation();
    if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) {
      setIsDragging(false);
    }
  };

  const handleDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    event.stopPropagation();
    setIsDragging(false);
    const file = event.dataTransfer.files[0];
    if (file) processUploadedFile(file);
  };

  const grade = result ? getGrade(result.totalScore) : null;
  const beatPct = result ? (result.totalScore >= 95 ? 97 : result.totalScore >= 88 ? 92 : result.totalScore >= 80 ? 85 : 72) : 0;
  const compactTitle = uploadedFile?.name?.replace(/\.[^.]+$/, "") ?? "剧本评测";
  const scriptMinutes = Math.max(8, Math.round(script.length / 460));
  const reportDate = new Date().toLocaleDateString("zh-CN", { month: "2-digit", day: "2-digit" });
  const statusClass = loading ? "is-loading" : result ? "is-complete" : hasContent ? "is-ready" : "is-idle";

  return (
    <section className={`script-eval-v5 page-motion ${statusClass}`}>
      <div className="script-eval-v5-page">
        {/* Left Panel */}
        <aside className="script-eval-v5-left">
          <div className="script-eval-v5-left-main">
            <div className="script-eval-v5-lp-section">
              <div className="script-eval-v5-lp-label">上传剧本</div>
              <div
                className={`script-eval-v5-upload-zone${isDragging ? " is-dragging" : ""}`}
                role="button"
                tabIndex={0}
                onClick={() => fileInputRef.current?.click()}
                onKeyDown={uploadKeyDown}
                onDragOver={handleDragOver}
                onDragLeave={handleDragLeave}
                onDrop={handleDrop}
              >
                {isDragging ? (
                  <div className="script-eval-v5-upload-drop-overlay">
                    <UploadOutlined />
                    <span>释放文件以上传</span>
                  </div>
                ) : null}
                {uploadedFile ? (
                  <div className="script-eval-v5-upload-done is-show">
                    <button
                      type="button"
                      className="script-eval-v5-upload-delete"
                      onClick={(e) => { e.stopPropagation(); handleReset(); }}
                      aria-label="删除文件"
                    >
                      <CloseOutlined />
                    </button>
                    <CheckCircleFilled />
                    <span className="script-eval-v5-uf-meta">
                      <span className="script-eval-v5-uf-name">{uploadedFile.name}</span>
                      <span className="script-eval-v5-uf-size">{formatFileSize(uploadedFile.size)}</span>
                    </span>
                    <span className="script-eval-v5-uf-re" onClick={(e) => { e.stopPropagation(); handleReset(); }}>
                      重新上传
                    </span>
                  </div>
                ) : (
                  <>
                    <div className="script-eval-v5-upload-icon"><ShellIcon name="upload" /></div>
                    <div className="script-eval-v5-upload-text">拖拽或点击上传</div>
                    <button type="button" className="script-eval-v5-upload-btn" onClick={(e) => { e.stopPropagation(); fileInputRef.current?.click(); }}>
                      <ShellIcon name="upload" /> 选择剧本
                    </button>
                    <div className="script-eval-v5-upload-hint">{TEXT_FILE_HINT}</div>
                  </>
                )}
              </div>
              <input ref={fileInputRef} type="file" accept={TEXT_FILE_ACCEPT} style={{ display: "none" }} onChange={handleFileUpload} />
            </div>

            <div className="script-eval-v5-lp-section">
              <div className="script-eval-v5-lp-label">AI 识别信息</div>
              <div className="script-eval-v5-info-grid">
                {!result ? (
                  <div className="script-eval-v5-info-empty">上传剧本并点击评测后识别</div>
                ) : (
                  <>
                    <div className="script-eval-v5-info-item">
                      <span className="script-eval-v5-info-key">综合评分</span>
                      <span className="script-eval-v5-info-val"><span className="script-eval-v5-info-tag">{result.totalScore}分 · {grade}级</span></span>
                    </div>
                    <div className="script-eval-v5-info-item">
                      <span className="script-eval-v5-info-key">文本长度</span>
                      <span className="script-eval-v5-info-val">{script.length} 字</span>
                    </div>
                    <div className="script-eval-v5-info-item">
                      <span className="script-eval-v5-info-key">评测时间</span>
                      <span className="script-eval-v5-info-val">{new Date().toLocaleDateString("zh-CN")}</span>
                    </div>
                    <div className="script-eval-v5-info-item">
                      <span className="script-eval-v5-info-key">击败比例</span>
                      <span className="script-eval-v5-info-val">{beatPct}%</span>
                    </div>
                  </>
                )}
              </div>
            </div>

            <div className="script-eval-v5-lp-section is-fill">
              <div className="script-eval-v5-lp-label">历史评测</div>
              <div className="script-eval-v5-history-list">
                {!session ? (
                  <div className="script-eval-v5-history-empty">登录后查看云端评测记录</div>
                ) : history.length === 0 ? (
                  <div className="script-eval-v5-history-empty">暂无评测记录</div>
                ) : (
                  history.map((item, i) => (
                    <div key={i} className={`script-eval-v5-history-item${i === activeHistoryIndex ? " is-active" : ""}`}
                      onClick={() => handleHistoryClick(item, i)} role="button" tabIndex={0}
                      onKeyDown={(e) => { if ((e as React.KeyboardEvent).key === "Enter") handleHistoryClick(item, i); }}>
                      <div className="script-eval-v5-hi-left">
                        <div className="script-eval-v5-hi-name">{item.name}</div>
                        <div className="script-eval-v5-hi-date">{item.date}</div>
                        <div className="script-eval-v5-hi-bar">
                          <div className="script-eval-v5-hi-bar-fill" style={{ width: `${Math.min(92, (item.score / 100) * 100)}%` }} />
                        </div>
                      </div>
                      <div className="script-eval-v5-hi-right">
                        <div className={`script-eval-v5-hi-score${item.score >= 90 ? " is-green" : ""}`}>{item.score}</div>
                        <div className="script-eval-v5-hi-grade">{item.grade}</div>
                      </div>
                    </div>
                  ))
                )}
              </div>
            </div>

            <div className="script-eval-v5-lp-bottom">
              <button
                type="button"
                className="script-eval-v5-eval-btn"
                disabled={loading || !hasContent}
                onClick={() => void handleEvaluate()}
              >
                {loading ? <ShellIcon name="loading" /> : <ShellIcon name="thunderbolt" />}
                <span>{loading ? "评测中..." : "开始评测"}</span>
              </button>
              <button type="button" className="script-eval-v5-export-btn" disabled={!result} onClick={handleExportMarkdown}>
                <ShellIcon name="download" />
                <span>导出评测报告</span>
              </button>
            </div>
          </div>
        </aside>

        {/* Right Area */}
        <div className="script-eval-v5-right">
          <div className="script-eval-v5-right-topbar">
            <div className="script-eval-v5-right-title">
              <span className="script-eval-v5-rt-green">剧本评测</span>
              {uploadedFile && <> · {compactTitle}</>}
            </div>
            <div className="script-eval-v5-right-actions">
              {result && (
                <>
                  <button type="button" className="script-eval-v5-action-btn" onClick={() => void handleCopyReport()}>
                    <ShellIcon name="copy" />{copied ? "已复制" : "复制"}
                  </button>
                  <button type="button" className="script-eval-v5-action-btn" onClick={handleExportMarkdown}>
                    <ShellIcon name="download" />导出
                  </button>
                </>
              )}
            </div>
          </div>

          <div className={`script-eval-v5-right-content${result ? " is-report" : ""}`}>
            {loading ? (
              <div className="script-eval-v5-input-section">
                <div className="script-eval-v5-illustration" aria-label="评测中">
                  <div className="script-eval-v5-loading">
                    <div className="page-loading-spinner" />
                    <strong>AI 正在分析剧本...</strong>
                    <p>正在调用模型进行六维评分，预计需要 15-30 秒</p>
                    <div className="script-eval-v5-loading-steps" aria-hidden="true">
                      <span>结构识别</span>
                      <span>冲突评估</span>
                      <span>商业潜力</span>
                    </div>
                  </div>
                </div>
              </div>
            ) : !result && (
              <div className="script-eval-v5-input-section">
                <div className={`script-eval-v5-illustration${isDragging ? " is-dragging" : ""}`} aria-label="上传剧本并开始评测">
                  <div
                    className="script-eval-v5-illustration-hit"
                    role="button"
                    tabIndex={0}
                    onClick={() => fileInputRef.current?.click()}
                    onKeyDown={uploadKeyDown}
                    onDragOver={handleDragOver}
                    onDragLeave={handleDragLeave}
                    onDrop={handleDrop}
                  >
                    {isDragging && (
                      <div className="script-eval-v5-upload-drop-overlay">
                        <UploadOutlined />
                        <span>释放文件以上传</span>
                      </div>
                    )}
                    <div className="script-eval-v5-upload-card-icon">
                      <ShellIcon name="file-text" />
                    </div>
                    <div className="script-eval-v5-upload-card-title">
                      {uploadedFile ? "剧本已导入" : "上传剧本文件"}
                    </div>
                    <div className="script-eval-v5-upload-card-desc">
                      {uploadedFile
                        ? "如需更换，点击此处重新上传；完成后点击左侧开始评测。"
                        : `${TEXT_FILE_HINT}，上传后点击开始评测，AI 将识别剧本信息。`}
                    </div>
                  </div>
                </div>

                {evalError && (
                  <div className="script-eval-v5-error" role="alert">
                    <span>评测失败</span><span>{evalError}</span>
                    <button type="button" className="script-eval-v5-retry-btn" onClick={() => void handleEvaluate()} disabled={!hasContent}>
                      重试
                    </button>
                  </div>
                )}
              </div>
            )}

            {result && (
              <section className="script-eval-report script-eval-report--inside">
                <div className="script-eval-report__body">
                  <header className="script-eval-report__hero">
                    <div className="script-eval-report__score-block">
                      <div className="script-eval-report__score-row">
                        <span className="script-eval-report__score">{animatedScore}</span>
                        <span className="script-eval-report__score-total">/ 100</span>
                        <span className="script-eval-report__grade">
                          <i />
                          {grade}级
                        </span>
                      </div>
                      <div className="script-eval-report__score-line">
                        <span style={{ width: `${animatedScore}%` }} />
                      </div>
                      <div className="script-eval-report__beat">{result.totalScore >= 90 ? "优秀" : result.totalScore >= 80 ? "良好" : result.totalScore >= 70 ? "中等" : "待提升"}，{result.totalScore >= 85 ? "具备商业开发潜力" : "建议针对性优化后再提交"}</div>
                    </div>

                    <div className="script-eval-report__summary">
                      <div className="script-eval-report__title-line">
                        <div>
                          <h1>{compactTitle}</h1>
                          <p>{`剧本评测 · ${scriptMinutes} min · ${reportDate}`}</p>
                        </div>
                      </div>
                      <p className="script-eval-report__desc">{result.summary}</p>
                    </div>
                  </header>

                  <section className="script-eval-report__chart-card" aria-label="维度拆解">
                    <div className="script-eval-report__card-head">
                      <span><i />维度拆解</span>
                      <div className="script-eval-report__legend">
                        <span><i className="is-score" />得分</span>
                        <span><i className="is-loss" />扣分</span>
                      </div>
                    </div>
                    <div className="script-eval-report__chart">
                      <div className="script-eval-report__axis">
                        <span>100%</span>
                        <span>80%</span>
                        <span>60%</span>
                        <span>40%</span>
                        <span>20%</span>
                        <span>0%</span>
                      </div>
                      <div className="script-eval-report__chart-grid">
                        {SCORE_DIMENSIONS.map((dim, dimIndex) => {
                          const score = getDimensionScore(result, dim);
                          const pct = Math.max(0, Math.min(1, score / dim.maxScore));
                          const lossPct = 1 - pct;
                          const isPerfect = score === dim.maxScore;
                          const isActive = activeDim === null || activeDim === dimIndex;
                          return (
                            <button
                              key={dim.key}
                              type="button"
                              className={`script-eval-report__bar-col${isActive ? "" : " is-dimmed"}`}
                              onMouseEnter={() => setActiveDim(dimIndex)}
                              onFocus={() => setActiveDim(dimIndex)}
                              onMouseLeave={() => setActiveDim(null)}
                              onBlur={() => setActiveDim(null)}
                              aria-label={`${dim.label} ${score}/${dim.maxScore}，${dim.hint}`}
                            >
                              <div className="script-eval-report__bar-score">
                                <b>{score}</b><small>/{dim.maxScore}</small>{isPerfect ? <em>*</em> : null}
                              </div>
                              <div className="script-eval-report__bar-box">
                                {lossPct > 0 ? <div className="script-eval-report__bar-loss" style={{ height: `${lossPct * 100}%` }} /> : null}
                                <div className="script-eval-report__bar-fill" style={{ height: `${pct * 100}%` }} />
                              </div>
                              <strong>{dim.label}</strong>
                              <span>{dim.hint}</span>
                            </button>
                          );
                        })}
                      </div>
                    </div>
                    <div className="script-eval-report__chart-note">
                      <ShellIcon name="bar-chart" />
                      <span>
                        {activeDim === null
                          ? "悬停维度可查看当前分项表现，优先从低分项制定改稿计划。"
                          : `${SCORE_DIMENSIONS[activeDim].label}：${SCORE_DIMENSIONS[activeDim].detail}`}
                      </span>
                    </div>
                  </section>

                  <div className="script-eval-report__detail-grid">
                    {SCORE_DIMENSIONS.map((dim) => {
                      const score = getDimensionScore(result, dim);
                      const pct = Math.round((score / dim.maxScore) * 100);
                      const subScores = getDimensionSubScores(result, dim);
                      const evidence = getDimensionEvidence(result, dim);

                      return (
                        <section className="script-eval-report__detail-card" key={dim.key}>
                          <header className="script-eval-report__detail-head">
                            <div>
                              <span>{dim.label}</span>
                              <strong>{score}<small>/{dim.maxScore}</small></strong>
                            </div>
                            <em>{pct}%</em>
                          </header>
                          <p className="script-eval-report__detail-hint">{dim.hint}</p>
                          {subScores.length > 0 ? (
                            <div className="script-eval-report__subscore-list">
                              {subScores.map(([key, value]) => {
                                const subPct = Math.max(0, Math.min(100, Math.round((value / dim.maxScore) * 100)));
                                return (
                                  <div className="script-eval-report__subscore-row" key={key}>
                                    <span>{formatSubScoreLabel(key)}</span>
                                    <div className="script-eval-report__subscore-bar" aria-hidden="true">
                                      <i style={{ width: `${subPct}%` }} />
                                    </div>
                                    <b>{value}</b>
                                  </div>
                                );
                              })}
                            </div>
                          ) : (
                            <p className="script-eval-report__detail-empty">等待模型返回更细的子项评分；当前先按主维度分数展示。</p>
                          )}
                          {evidence.length > 0 ? (
                            <ul className="script-eval-report__evidence-list">
                              {evidence.map((item, index) => <li key={index}>{item}</li>)}
                            </ul>
                          ) : null}
                        </section>
                      );
                    })}
                  </div>

                  <div className="script-eval-report__findings">
                    {result.highlights.length > 0 ? (
                      <section className="script-eval-report__finding-group is-highlight">
                        <h2>亮点 <span>{result.highlights.length}</span></h2>
                        <div>
                          {result.highlights.map((item, index) => (
                            <p key={index}>{item}</p>
                          ))}
                        </div>
                      </section>
                    ) : null}
                    {result.issues.length > 0 ? (
                      <section className="script-eval-report__finding-group is-issue">
                        <h2>扣分点 <span>{result.issues.length}</span></h2>
                        <div>
                          {result.issues.map((item, index) => (
                            <p key={index}>{item}</p>
                          ))}
                        </div>
                      </section>
                    ) : null}
                  </div>

                  {result.suggestions.length > 0 ? (
                    <section className="script-eval-report__path-card">
                      <div className="script-eval-report__card-head">
                        <span><i />优化路径</span>
                      </div>
                      <table className="script-eval-report__path-table">
                        <thead>
                          <tr>
                            <th>#</th>
                            <th>类型</th>
                            <th>优化路径</th>
                            <th>优先级</th>
                          </tr>
                        </thead>
                        <tbody>
                          {result.suggestions.map((item, index) => {
                            const high = index < 2;
                            return (
                              <tr key={index}>
                                <td>{String(index + 1).padStart(2, "0")}</td>
                                <td>{high ? "核心" : "增强"}</td>
                                <td>{item}</td>
                                <td><span className={high ? "is-high" : "is-mid"}>{high ? "HIGH" : "MID"}</span></td>
                              </tr>
                            );
                          })}
                        </tbody>
                      </table>
                    </section>
                  ) : null}
                </div>
              </section>
            )}
          </div>

          <div className="script-eval-v5-statusbar">
            <div className="script-eval-v5-status-dot" />
            <span>{loading ? "评测中..." : result ? "评测完成" : hasContent ? "待评测" : "等待上传"}</span>
            <span className="script-eval-v5-sb-right">{result ? `六维标准 · ${result.totalScore}分` : "六维标准"}</span>
          </div>
        </div>
      </div>
    </section>
  );
}

export default ScriptTokensPage;
