/* ────────────────────────────────────────────────────────────
   pipeline.jsx — 7단계 파이프라인 대시보드
   PIPELINE-DASHBOARD.md §4/§5/§6 · DESIGN-DECISIONS A17–A20.
   과정(course) 선택 → 가로 7단계 Stepper → StageCard(상태 배지·열기→·완료/스킵) → ProgressBar.
   차시 그리드(DashboardView)와 토글로 공존 — 미배정 차시는 그대로 그리드에서 동작.
   서버=SSOT. 모든 데이터는 /api/courses(courses.js). 토큰 변수만, 해요체, 이모지 없음.
   낙관적 전이 + expectedStatus(409→리로드). 15초 폴링으로 로컬 상태 일괄 갱신.
   ──────────────────────────────────────────────────────────── */

/* ── 7단계 메타(서버 lib/pipeline.js 정본 미러). label/group/열기 타깃. ── */
const PIPELINE_STAGES = [
  { key: "course_intake",    position: 1, label: "과정섭외",     group: "과정섭외", open: "intake" },
  { key: "manuscript_qc",    position: 2, label: "원고품질검토", group: "원고",     open: "manuscript" },
  { key: "manuscript_cpr",   position: 3, label: "원고CPR",      group: "원고",     open: "manuscript" },
  { key: "manuscript_final", position: 4, label: "원고최종화",   group: "원고",     open: "manuscript" },
  { key: "ppt_production",   position: 5, label: "PPT제작",      group: "PPT제작",  open: "ppt" },
  { key: "sb_review",        position: 6, label: "SB검수기",     group: "QA",       open: "review" },
  { key: "dev_review",       position: 7, label: "개발물검수",   group: "QA",       open: "review" },
];
const STAGE_META_BY_KEY = PIPELINE_STAGES.reduce((m, s) => ((m[s.key] = s), m), {});

/* 그룹 밴드(헤더) — 과정섭외(1) · 원고(2–4) · PPT제작(5) · QA(6–7). */
const STAGE_GROUPS = [
  { label: "과정섭외", span: 1 },
  { label: "원고",     span: 3 },
  { label: "PPT제작",  span: 1 },
  { label: "QA",       span: 2 },
];

/* 상태 배지 메타(§5.2). 존재하는 토큰 변수로 매핑. */
const STAGE_STATUS = {
  pending:     { label: "대기",   color: "var(--label-alt)",     bg: "var(--bg-alt)",       dot: "var(--label-disable)", muted: false },
  in_progress: { label: "진행중", color: "var(--primary)",       bg: "var(--primary-light)", dot: "var(--primary)",       muted: false },
  done:        { label: "완료",   color: "var(--positive)",      bg: "var(--positive-bg)",  dot: "var(--positive)",      muted: false },
  skipped:     { label: "스킵",   color: "var(--label-alt)",     bg: "var(--bg-neutral)",   dot: "var(--label-disable)", muted: true },
};
function stageStatusMeta(s) { return STAGE_STATUS[s] || STAGE_STATUS.pending; }

/* M1 currentStageKey: position 최소이면서 status ∈ {in_progress, pending}. 서버 값과 동일 규칙(폴백용). */
function deriveCurrentStageKey(stages) {
  const open = (stages || [])
    .filter((s) => s && (s.status === "in_progress" || s.status === "pending"))
    .sort((a, b) => (a.position || 0) - (b.position || 0));
  return open.length ? open[0].stageKey : null;
}

/* ════════════════════════════════════════════════════════════════
   StatusBadge — 단계 상태 배지(점 + 라벨).
   ════════════════════════════════════════════════════════════════ */
function StageStatusBadge({ status }) {
  const m = stageStatusMeta(status);
  return React.createElement(Pill, { color: m.color, bg: m.bg },
    React.createElement("span", { style: { width: 6, height: 6, borderRadius: "50%", background: m.dot } }),
    m.label);
}

/* ════════════════════════════════════════════════════════════════
   StageCard — 상태 배지 + hint + 열기→ + 완료/스킵(+ 시작/되돌리기).
   status별 버튼(§5.2):
     pending     → [시작][스킵]
     in_progress → [열기→][완료][스킵]
     done        → [열기→][되돌리기]
     skipped     → [되돌리기]
   currentStageKey 면 --primary 링.
   ════════════════════════════════════════════════════════════════ */
function StageCard({ stage, isCurrent, busy, onTransition, onOpen }) {
  const s = stage || {};
  const meta = STAGE_META_BY_KEY[s.stageKey] || {};
  const stMeta = stageStatusMeta(s.status);
  const label = s.label || meta.label || s.stageKey || "단계";
  const showOpen = s.status === "in_progress" || s.status === "done";

  const ringStyle = isCurrent
    ? { borderColor: "var(--primary)", boxShadow: "0 0 0 3px var(--primary-light)" }
    : { borderColor: "var(--border)" };

  return React.createElement("div", {
    style: {
      flex: "1 1 0", minWidth: 158, maxWidth: 240,
      background: "var(--bg-normal)", border: "1px solid var(--border)", borderRadius: "var(--r-lg)",
      padding: "13px 13px 12px", display: "flex", flexDirection: "column", gap: 9,
      transition: "border-color var(--t-fast), box-shadow var(--t-fast)",
      opacity: stMeta.muted ? 0.78 : 1,
      ...ringStyle,
    },
  },
    /* 헤더: position 칩 + 단계명 + 상태 배지 */
    React.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 6 } },
      React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 7, minWidth: 0 } },
        React.createElement("span", {
          style: {
            flex: "0 0 auto", width: 19, height: 19, borderRadius: "var(--r-xs)",
            background: isCurrent ? "var(--primary)" : "var(--bg-alt)",
            color: isCurrent ? "#fff" : "var(--label-alt)",
            fontSize: 11, fontWeight: 700, display: "inline-flex", alignItems: "center", justifyContent: "center",
          },
        }, String(meta.position || s.position || "")),
        React.createElement("span", {
          className: "t-label-1",
          style: {
            color: stMeta.muted ? "var(--label-alt)" : "var(--label-normal)", fontWeight: 700,
            textDecoration: stMeta.muted ? "line-through" : "none",
            overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
          },
        }, label)),
      React.createElement(StageStatusBadge, { status: s.status })),

    /* hint 서브텍스트(읽기전용, 서버 §6.4). 없으면 자리만 안 차지. */
    s.hint && s.hint.text
      ? React.createElement("div", { className: "t-caption-1", style: { color: "var(--label-assist)", lineHeight: 1.4 } }, s.hint.text)
      : null,

    /* 버튼 행 */
    React.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginTop: 2 } },
      showOpen
        ? React.createElement(Button, { kind: "outline", size: "sm", disabled: busy, onClick: () => onOpen(s) }, "열기→")
        : null,
      s.status === "pending"
        ? React.createElement(Button, { kind: "primary", size: "sm", disabled: busy, onClick: () => onTransition(s, "start") }, "시작")
        : null,
      (s.status === "pending" || s.status === "in_progress" || s.status === "skipped")
        ? React.createElement(Button, { kind: s.status === "in_progress" ? "primary" : "outline", size: "sm", disabled: busy, onClick: () => onTransition(s, "complete") }, "완료")
        : null,
      (s.status === "pending" || s.status === "in_progress")
        ? React.createElement(Button, { kind: "ghost", size: "sm", disabled: busy, onClick: () => onTransition(s, "skip") }, "스킵")
        : null,
      (s.status === "done" || s.status === "skipped")
        ? React.createElement(Button, { kind: "ghost", size: "sm", disabled: busy, onClick: () => onTransition(s, "reopen") }, "되돌리기")
        : null),

    /* 누가·언제(있을 때만) */
    s.actedBy && s.actedBy.name
      ? React.createElement("div", { className: "t-caption-1", style: { color: "var(--label-assist)" } },
          `${s.actedBy.name} · ${fmtDate(s.actedAt)}`)
      : null
  );
}

/* ════════════════════════════════════════════════════════════════
   PipelineStepper — 그룹 밴드 + 7개 StageCard 가로 한 줄(narrow→flexWrap) + 셰브론.
   ════════════════════════════════════════════════════════════════ */
function PipelineStepper({ stages, currentStageKey, busyKey, onTransition, onOpen }) {
  const list = Array.isArray(stages) ? stages : [];
  return React.createElement("div", { style: { marginTop: 18 } },
    /* 그룹 밴드 — 카드 폭과 대략 비례하도록 flex span. narrow 에선 자연 wrap. */
    React.createElement("div", { style: { display: "flex", gap: 8, marginBottom: 7, flexWrap: "wrap" } },
      STAGE_GROUPS.map((g) =>
        React.createElement("div", {
          key: g.label,
          style: { flex: `${g.span} 1 0`, minWidth: g.span * 158 },
        },
          React.createElement("span", { className: "t-caption-1", style: { color: "var(--label-assist)", fontWeight: 700, letterSpacing: "0.01em" } }, g.label)))),

    /* 카드 행 */
    React.createElement("div", { style: { display: "flex", alignItems: "stretch", gap: 8, flexWrap: "wrap" } },
      list.map((st, i) =>
        React.createElement(React.Fragment, { key: st.stageKey || i },
          React.createElement(StageCard, {
            stage: st,
            isCurrent: st.stageKey === currentStageKey,
            busy: busyKey === st.stageKey,
            onTransition, onOpen,
          }),
          i < list.length - 1
            ? React.createElement("span", {
                style: { flex: "0 0 auto", alignSelf: "center", color: "var(--label-disable)", fontSize: 14, fontWeight: 700 },
              }, "›")
            : null)))
  );
}

/* ════════════════════════════════════════════════════════════════
   ProgressBar(M2) — done-only fill(--positive) + 스킵은 구분된 muted 세그먼트.
   라벨 = progress.label("1완료·2스킵/7"). 스킵은 완료로 카운트하지 않음.
   ════════════════════════════════════════════════════════════════ */
function ProgressBar({ progress }) {
  const p = progress || { done: 0, skipped: 0, total: 7, label: "0완료·0스킵/7", pct: 0 };
  const total = p.total || 7;
  const donePct = total ? (p.done / total) * 100 : 0;
  const skipPct = total ? (p.skipped / total) * 100 : 0;
  return React.createElement("div", { style: { marginTop: 22 } },
    React.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 7 } },
      React.createElement("span", { className: "t-label-2", style: { color: "var(--label-neutral)", fontWeight: 700 } }, "진행률"),
      React.createElement("span", { className: "t-label-2 tnum", style: { color: "var(--label-neutral)", fontWeight: 700 } }, p.label)),
    React.createElement("div", {
      style: {
        display: "flex", width: "100%", height: 10, borderRadius: "var(--r-full)",
        background: "var(--bg-alt)", overflow: "hidden", border: "1px solid var(--line-normal)",
      },
    },
      /* 완료 세그먼트(positive) */
      donePct > 0 ? React.createElement("div", {
        style: { width: `${donePct}%`, background: "var(--positive)", transition: "width var(--t-fast)" },
      }) : null,
      /* 스킵 세그먼트(muted, 줄무늬로 '완료 아님'을 시각 구분) */
      skipPct > 0 ? React.createElement("div", {
        style: {
          width: `${skipPct}%`,
          background: "repeating-linear-gradient(45deg, var(--label-disable), var(--label-disable) 3px, var(--bg-neutral) 3px, var(--bg-neutral) 6px)",
          transition: "width var(--t-fast)",
        },
      }) : null)
  );
}

/* ════════════════════════════════════════════════════════════════
   CourseSelect(§5.4) — 네이티브 select(과정 선택) + "새 과정".
   옵션 라벨 "{name} — {progressLabel}". 빈 목록 → StateMsg CTA.
   ════════════════════════════════════════════════════════════════ */
function CourseSelect({ courses, value, onChange, onNew, loading }) {
  const list = Array.isArray(courses) ? courses : [];
  return React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" } },
    React.createElement("label", { className: "t-label-2", style: { color: "var(--label-neutral)", fontWeight: 700 } }, "과정 선택"),
    React.createElement("div", { style: { position: "relative", minWidth: 240 } },
      React.createElement("select", {
        value: value || "",
        onChange: (e) => onChange(e.target.value),
        disabled: loading || list.length === 0,
        style: {
          appearance: "none", WebkitAppearance: "none",
          height: 40, padding: "0 34px 0 13px", width: "100%",
          background: "var(--bg-normal)", color: "var(--label-normal)",
          border: "1px solid var(--border)", borderRadius: "var(--r-sm)",
          fontSize: 14, fontWeight: 600, cursor: list.length ? "pointer" : "default",
        },
      },
        list.length === 0
          ? React.createElement("option", { value: "" }, "과정이 없어요")
          : list.map((c) =>
              React.createElement("option", { key: c.courseId, value: c.courseId },
                `${c.name}${c.progress && c.progress.label ? " — " + c.progress.label : ""}`))),
      React.createElement("span", {
        style: { position: "absolute", right: 12, top: "50%", transform: "translateY(-50%)", pointerEvents: "none", color: "var(--label-assist)", fontSize: 11 },
      }, "▼")),
    React.createElement(Button, { kind: "outline", size: "md", onClick: onNew },
      React.createElement("svg", { width: 15, height: 15, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.4, strokeLinecap: "round" }, React.createElement("path", { d: "M12 5v14M5 12h14" })),
      "새 과정")
  );
}

/* ════════════════════════════════════════════════════════════════
   NewCourseModal — POST /api/courses { name, clientName, note }.
   NewProjectModal 패턴 미러.
   ════════════════════════════════════════════════════════════════ */
function NewCourseModal({ onClose, onCreated }) {
  const [name, setName] = useState("");
  const [clientName, setClientName] = useState("");
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");

  async function submit(e) {
    e.preventDefault();
    setErr(""); setBusy(true);
    try {
      const data = await api("/courses", {
        method: "POST",
        body: { name: name.trim(), clientName: clientName.trim() || null, note: null },
      });
      onCreated(data && data.course ? data.course : null);
    } catch (ex) {
      setErr((ex && ex.message) || "과정을 만들지 못했어요.");
    } finally { setBusy(false); }
  }

  return React.createElement("div", {
    onClick: onClose,
    style: { position: "fixed", inset: 0, background: "rgba(20,25,30,0.42)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 100, padding: 24 },
  },
    React.createElement("div", {
      onClick: (e) => e.stopPropagation(),
      style: { width: "100%", maxWidth: 460, background: "var(--bg-normal)", borderRadius: "var(--r-lg)", boxShadow: "var(--sh-3)", padding: "26px 26px 24px" },
    },
      React.createElement("div", { className: "t-heading-1", style: { marginBottom: 4 } }, "새 과정 만들기"),
      React.createElement("p", { className: "t-body-2", style: { color: "var(--label-alt)", margin: "0 0 20px" } }, "과정명을 입력하면 7단계 파이프라인이 만들어져요."),
      React.createElement("form", { onSubmit: submit },
        React.createElement(Field, { label: "과정명", value: name, onChange: (e) => setName(e.target.value), placeholder: "예) 산업안전보건교육", autoFocus: true }),
        React.createElement(Field, { label: "발주처", value: clientName, onChange: (e) => setClientName(e.target.value), placeholder: "예) ○○공단 (선택)" }),
        err ? React.createElement("div", { className: "t-label-2", style: { color: "var(--negative)", background: "var(--negative-bg)", borderRadius: "var(--r-sm)", padding: "9px 12px", marginBottom: 14 } }, err) : null,
        React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 8 } },
          React.createElement(Button, { type: "button", kind: "outline", onClick: onClose }, "취소"),
          React.createElement(Button, { type: "submit", disabled: busy },
            busy ? React.createElement(Spinner, { s: 15, color: "#fff" }) : "만들기"))
      )
    )
  );
}

/* ════════════════════════════════════════════════════════════════
   ChapterPickerModal(§5.2) — 다(多)차시 열기→ 시 차시 선택.
   GET /api/courses/:id/projects → 선택한 차시를 editor/review 로 라우트.
   1-차시면 호출부가 picker 생략. 0-차시면 안내 + 그리드 안내.
   ════════════════════════════════════════════════════════════════ */
function ChapterPickerModal({ projects, stageLabel, degradeNote, onPick, onClose }) {
  const list = Array.isArray(projects) ? projects : [];
  return React.createElement("div", {
    onClick: onClose,
    style: { position: "fixed", inset: 0, background: "rgba(20,25,30,0.42)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 100, padding: 24 },
  },
    React.createElement("div", {
      onClick: (e) => e.stopPropagation(),
      style: { width: "100%", maxWidth: 480, maxHeight: "80vh", overflow: "auto", background: "var(--bg-normal)", borderRadius: "var(--r-lg)", boxShadow: "var(--sh-3)", padding: "24px 24px 20px" },
    },
      React.createElement("div", { className: "t-heading-1", style: { marginBottom: 4 } }, `${stageLabel} — 차시 선택`),
      React.createElement("p", { className: "t-body-2", style: { color: "var(--label-alt)", margin: "0 0 14px" } }, "열어볼 차시를 선택해 주세요."),
      degradeNote
        ? React.createElement("div", { className: "t-label-2", style: { color: "var(--caution)", background: "var(--caution-bg)", borderRadius: "var(--r-sm)", padding: "9px 12px", marginBottom: 14 } }, degradeNote)
        : null,
      list.length === 0
        ? React.createElement("div", { className: "t-body-2", style: { color: "var(--label-alt)", padding: "18px 0 8px", textAlign: "center" } },
            "이 과정에 차시가 없어요 — 차시 탭에서 차시를 먼저 추가해 주세요.")
        : React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8 } },
            list.map((p) =>
              React.createElement("button", {
                key: p.id,
                onClick: () => onPick(p),
                style: {
                  textAlign: "left", background: "var(--bg-normal)", border: "1px solid var(--border)", borderRadius: "var(--r-md)",
                  padding: "12px 14px", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10,
                },
                onMouseEnter: (e) => { e.currentTarget.style.borderColor = "var(--primary)"; },
                onMouseLeave: (e) => { e.currentTarget.style.borderColor = "var(--border)"; },
              },
                React.createElement("div", { style: { minWidth: 0 } },
                  React.createElement("div", { className: "t-label-1", style: { color: "var(--label-normal)", fontWeight: 700, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, p.chapterName || "제목 없는 차시"),
                  React.createElement("div", { className: "t-caption-1 tnum", style: { color: "var(--label-assist)", marginTop: 2 } }, `슬라이드 ${typeof p.slideCount === "number" ? p.slideCount : 0}장`)),
                React.createElement("span", { style: { color: "var(--label-disable)", fontSize: 16 } }, "›"))) ),
      React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", marginTop: 16 } },
        React.createElement(Button, { kind: "outline", onClick: onClose }, "닫기"))
    )
  );
}

/* ════════════════════════════════════════════════════════════════
   CourseDetailModal — 과정섭외(course_intake) 열기→ 대상(차시 없는 detail).
   ════════════════════════════════════════════════════════════════ */
function CourseDetailModal({ course, projectCount, onClose }) {
  const c = course || {};
  return React.createElement("div", {
    onClick: onClose,
    style: { position: "fixed", inset: 0, background: "rgba(20,25,30,0.42)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 100, padding: 24 },
  },
    React.createElement("div", {
      onClick: (e) => e.stopPropagation(),
      style: { width: "100%", maxWidth: 440, background: "var(--bg-normal)", borderRadius: "var(--r-lg)", boxShadow: "var(--sh-3)", padding: "24px 24px 20px" },
    },
      React.createElement("div", { className: "t-heading-1", style: { marginBottom: 6 } }, c.name || "과정"),
      React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8, margin: "6px 0 16px" } },
        React.createElement("div", { className: "t-body-2", style: { color: "var(--label-alt)" } },
          React.createElement("span", { style: { color: "var(--label-assist)" } }, "발주처 "), c.clientName || "—"),
        React.createElement("div", { className: "t-body-2 tnum", style: { color: "var(--label-alt)" } },
          React.createElement("span", { style: { color: "var(--label-assist)" } }, "차시 "), `${typeof projectCount === "number" ? projectCount : 0}개`),
        c.note ? React.createElement("div", { className: "t-body-2", style: { color: "var(--label-alt)", whiteSpace: "pre-wrap" } }, c.note) : null),
      React.createElement("div", { style: { display: "flex", justifyContent: "flex-end" } },
        React.createElement(Button, { kind: "outline", onClick: onClose }, "닫기"))
    )
  );
}

/* ════════════════════════════════════════════════════════════════
   PipelineView — app.jsx route 'pipeline'.
   - GET /api/courses (dropdown 소스) → 선택 → GET /api/courses/:id (상세)
   - 15초 폴링으로 상세 일괄 갱신(M5)
   - 전이: 낙관적 + expectedStatus, 409→상세 리로드
   - 열기→: stage→surface 매핑(§5.2). review 미구현 시 editor 폴백 + 안내(graceful).
   ════════════════════════════════════════════════════════════════ */
