/* ============================================================
   ui.jsx — 共用元件 + 工具函式（掛到 window）
   ============================================================ */
const { useState, useEffect, useRef, useMemo } = React;

/* ---------- date helpers ---------- */
const DAY = 86400000;
function d(s) { return new Date(s + (s.length <= 10 ? "T00:00:00" : "")); }
function daysBetween(a, b) { return Math.round((d(b) - d(a)) / DAY); }
function fmtDate(s) {
  const x = d(s);
  return `${x.getMonth() + 1}/${x.getDate()}`;
}
function fmtFull(s) {
  const x = d(s);
  return `${x.getFullYear()}.${String(x.getMonth() + 1).padStart(2, "0")}.${String(x.getDate()).padStart(2, "0")}`;
}
const TODAY = "2026-05-30";

/* ---------- progress helpers ---------- */
function stageProgress(stage) {
  if (!stage.subtasks.length) return stage.status === "done" ? 1 : 0;
  return stage.subtasks.filter((s) => s.done).length / stage.subtasks.length;
}
function taskProgress(task) {
  const all = task.stages.flatMap((s) => s.subtasks);
  if (!all.length) return 0;
  return all.filter((s) => s.done).length / all.length;
}
function taskCounts(task) {
  const all = task.stages.flatMap((s) => s.subtasks);
  return { done: all.filter((s) => s.done).length, total: all.length };
}
function currentStage(task) {
  return task.stages.find((s) => s.status === "active" || s.status === "blocked")
    || task.stages.find((s) => s.status === "upcoming")
    || task.stages[task.stages.length - 1];
}
function isBlocked(task) {
  return task.stages.some((s) => s.status === "blocked") ||
    task.blockers.some((b) => !b.resolved && b.needsDecision);
}

const STAGE_COLOR = {
  spec: "var(--st-spec)", design: "var(--st-design)", develop: "var(--st-develop)",
  test: "var(--st-test)", review: "var(--st-review)", deploy: "var(--st-deploy)",
};
const PRIO = {
  high: { label: "高", color: "var(--block)", soft: "var(--block-soft)" },
  medium: { label: "中", color: "var(--warn)", soft: "var(--warn-soft)" },
  low: { label: "低", color: "var(--ink-3)", soft: "var(--surface-2)" },
};

/* ---------- icons (inline, stroke) ---------- */
function Icon({ name, size = 18, ...p }) {
  const paths = {
    grid: "M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z",
    gantt: "M8 6h11M5 6h.01M8 12h7M5 12h.01M8 18h13M5 18h.01",
    list: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01",
    alert: "M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z",
    plus: "M12 5v14M5 12h14",
    x: "M18 6 6 18M6 6l12 12",
    chevron: "M9 18l6-6-6-6",
    sun: "M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4",
    moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z",
    menu: "M3 6h18M3 12h18M3 18h18",
    send: "M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z",
    check: "M20 6 9 17l-5-5",
    clock: "M12 8v4l3 2M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z",
    calendar: "M8 2v4M16 2v4M3 10h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z",
    user: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z",
    flag: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1zM4 22v-7",
    spark: "M12 3l1.9 5.6L19.5 10l-5.6 1.4L12 17l-1.9-5.6L4.5 10l5.6-1.4z",
    msg: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z",
    inbox: "M22 12h-6l-2 3h-4l-2-3H2M5.5 5h13l3.5 7v6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-6z",
    beaker: "M9 3h6M10 3v5L5 19a2 2 0 0 0 2 3h10a2 2 0 0 0 2-3l-5-11V3M7 14h10",
    file: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M9 13h6M9 17h4",
    bug: "M8 6V4a4 4 0 0 1 8 0v2M5 9h14M6 9v6a6 6 0 0 0 12 0V9M3 13h3M18 13h3M4 18l2.5-1.5M20 18l-2.5-1.5",
    shield: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",
    moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z",
    layers: "M12 2 2 7l10 5 10-5zM2 17l10 5 10-5M2 12l10 5 10-5",
    search: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.3-4.3",
    edit: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7M18.5 2.5a2.1 2.1 0 0 1 3 3L12 15l-4 1 1-4z",
    trash: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6",
    arrowRight: "M5 12h14M13 5l7 7-7 7",
    wand: "M15 4V2M15 16v-2M8 9h2M20 9h2M17.8 11.8 19 13M17.8 6.2 19 5M3 21l9-9M12.2 6.2 11 5",
    dollar: "M12 1v22M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6",
    refresh: "M3 2v6h6M21 12A9 9 0 0 0 6 5.3L3 8M21 22v-6h-6M3 12a9 9 0 0 0 15 6.7l3-2.7",
    target: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM12 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z",
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d={paths[name]} />
    </svg>
  );
}

/* ---------- AI avatar ---------- */
function AIAvatar({ size = 28 }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: size * 0.32, flexShrink: 0,
      background: "linear-gradient(150deg, var(--accent), var(--st-review))",
      display: "grid", placeItems: "center", color: "white",
      boxShadow: "var(--shadow-sm)",
    }}>
      <svg width={size * 0.56} height={size * 0.56} viewBox="0 0 24 24" fill="none"
        stroke="currentColor" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round">
        <rect x="4" y="7" width="16" height="12" rx="3" />
        <path d="M12 3v4M9 13h.01M15 13h.01" />
      </svg>
    </div>
  );
}
function BossAvatar({ size = 28 }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: size * 0.32, flexShrink: 0,
      background: "var(--surface-2)", border: "1px solid var(--line)",
      display: "grid", placeItems: "center", color: "var(--ink-2)",
      fontWeight: 700, fontSize: size * 0.4,
    }}>老</div>
  );
}

/* ---------- progress ring ---------- */
function ProgressRing({ value, size = 46, stroke = 4, color = "var(--accent)", showText = true }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  return (
    <div style={{ position: "relative", width: size, height: size, flexShrink: 0 }}>
      <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--line)" strokeWidth={stroke} />
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke={color} strokeWidth={stroke}
          strokeLinecap="round" strokeDasharray={c} strokeDashoffset={c * (1 - value)}
          style={{ transition: "stroke-dashoffset 0.6s cubic-bezier(0.2,0.8,0.2,1)" }} />
      </svg>
      {showText && (
        <div className="mono" style={{
          position: "absolute", inset: 0, display: "grid", placeItems: "center",
          fontSize: size * 0.26, fontWeight: 600, color: "var(--ink)",
        }}>{Math.round(value * 100)}</div>
      )}
    </div>
  );
}

/* ---------- progress bar ---------- */
function Bar({ value, color = "var(--accent)", height = 6 }) {
  return (
    <div style={{ background: "var(--line)", borderRadius: 20, height, overflow: "hidden", width: "100%" }}>
      <div style={{
        width: `${value * 100}%`, height: "100%", background: color, borderRadius: 20,
        transition: "width 0.6s cubic-bezier(0.2,0.8,0.2,1)",
      }} />
    </div>
  );
}

/* ---------- stage badge ---------- */
function StageChip({ stage, active }) {
  const def = window.STAGE_DEFS.find((s) => s.key === stage.key);
  const color = STAGE_COLOR[stage.key];
  return (
    <span className="chip" style={{
      background: active ? color : "var(--surface-2)",
      color: active ? "white" : "var(--ink-2)",
      border: active ? "none" : "1px solid var(--line)",
    }}>
      <span className="dot" style={{ background: active ? "white" : color }} />
      {def.short}
    </span>
  );
}

function PriorityTag({ priority }) {
  const p = PRIO[priority];
  return (
    <span className="chip prio" style={{ background: p.soft, color: p.color }}>
      <Icon name="flag" size={11} /> {p.label}優先
    </span>
  );
}

function StatusChip({ task }) {
  if (isBlocked(task)) return <span className="chip" style={{ background: "var(--block-soft)", color: "var(--block)" }}><span className="dot" style={{ background: "var(--block)" }} />待決策</span>;
  const prog = taskProgress(task);
  if (prog >= 1) return <span className="chip" style={{ background: "var(--ok-soft)", color: "var(--ok)" }}><span className="dot" style={{ background: "var(--ok)" }} />已完成</span>;
  if (prog === 0) return <span className="chip" style={{ background: "var(--surface-2)", color: "var(--ink-3)" }}><span className="dot" style={{ background: "var(--idle)" }} />未開始</span>;
  return <span className="chip" style={{ background: "var(--accent-soft)", color: "var(--accent-2)" }}><span className="dot" style={{ background: "var(--accent)" }} />進行中</span>;
}

Object.assign(window, {
  Icon, AIAvatar, BossAvatar, ProgressRing, Bar, StageChip, PriorityTag, StatusChip,
  daysBetween, fmtDate, fmtFull, TODAY, d, DAY,
  stageProgress, taskProgress, taskCounts, currentStage, isBlocked,
  STAGE_COLOR, PRIO,
});
