/* ────────────────────────────────────────────────────────────
   masters.jsx — 마스터(.potx/.pptx) 관리 UI
   CONTRACT §3.5:
     POST   /api/masters            multipart field `file` (+opt name)
            -> 201 {master:{id,name,originalFilename,layouts:[{idx,name,placeholders:[...]}],themeColors:["#..."],slideSize:{w,h}}}
     GET    /api/masters            -> 200 {items:[{id,name,themeColors,slideSize,createdAt}]}
     GET    /api/masters/:id        -> 200 {master:{id,name,layouts,themeColors,slideSize,originalFilename}}
     DELETE /api/masters/:id        -> 200 {ok:true}
   CONTRACT §3.2:
     PATCH  /api/projects/:id {masterId} -> 200 {project} (마스터 지정/교체)
   해요체·이모지 없음·플랫·tokens 변수만.
   ──────────────────────────────────────────────────────────── */

/* EMU(914400 = 1in, 12700 = 1pt) → 슬라이드 크기 사람이 읽는 라벨 */
function emuToSize(slideSize) {
  if (!slideSize || typeof slideSize.w !== "number" || typeof slideSize.h !== "number") return null;
  const wIn = slideSize.w / 914400;
  const hIn = slideSize.h / 914400;
  if (!wIn || !hIn) return null;
  const ratio = wIn / hIn;
  let label = "";
  if (Math.abs(ratio - 16 / 9) < 0.04) label = "16:9";
  else if (Math.abs(ratio - 4 / 3) < 0.04) label = "4:3";
  else label = `${ratio.toFixed(2)}:1`;
  return { wIn: wIn.toFixed(2), hIn: hIn.toFixed(2), label };
}

/* 테마색 스와치 줄 */
function ColorSwatches({ colors, size = 18 }) {
  const arr = Array.isArray(colors) ? colors.filter((c) => typeof c === "string") : [];
  if (!arr.length) {
    return React.createElement("span", { className: "t-caption-1", style: { color: "var(--label-assist)" } }, "테마색 정보 없음");
  }
  return React.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center" } },
    arr.map((c, i) => React.createElement("span", {
      key: i,
      title: c,
      style: {
        width: size, height: size, borderRadius: "var(--r-xs)", background: c,
        border: "1px solid var(--border)", boxShadow: "inset 0 0 0 1px rgba(255,255,255,0.18)", flexShrink: 0,
      },
    }))
  );
}

/* 레이아웃 목록 미리보기 (이름 + 플레이스홀더 수) */
function LayoutList({ layouts }) {
  const arr = Array.isArray(layouts) ? layouts : [];
  if (!arr.length) {
    return React.createElement("span", { className: "t-caption-1", style: { color: "var(--label-assist)" } }, "레이아웃 정보 없음");
  }
  return React.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 } },
    arr.map((l, i) => {
      const phc = Array.isArray(l?.placeholders) ? l.placeholders.length : 0;
      return React.createElement("span", {
        key: (l && l.idx != null ? l.idx : i),
        className: "t-caption-1",
        style: {
          display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 9px", borderRadius: "var(--r-full)",
          background: "var(--bg-alt)", color: "var(--label-neutral)", fontWeight: 600, border: "1px solid var(--border)",
        },
      },
        React.createElement("span", { style: { color: "var(--label-assist)", fontVariantNumeric: "tabular-nums" } }, (l && l.idx != null ? l.idx : i)),
        (l && l.name) || "레이아웃",
        phc ? React.createElement("span", { className: "tnum", style: { color: "var(--label-assist)", fontWeight: 500 } }, `· 자리 ${phc}`) : null);
    })
  );
}

/* 마스터 미리보기 카드 — inspect 결과(레이아웃/테마색/슬라이드 크기) */
function MasterPreviewCard({ master, active, busy, onAssign, onDelete, onSelect, selected }) {
  if (!master) return null;
  const size = emuToSize(master.slideSize);
  return React.createElement("div", {
    onClick: onSelect ? () => onSelect(master) : undefined,
    style: {
      background: "var(--bg-normal)", border: "1px solid",
      borderColor: active ? "var(--primary)" : (selected ? "var(--info)" : "var(--border)"),
      borderRadius: "var(--r-lg)", padding: "16px 16px 14px",
      boxShadow: active ? "0 0 0 3px var(--primary-light)" : "var(--sh-1)",
      cursor: onSelect ? "pointer" : "default", transition: "border-color var(--t-fast), box-shadow var(--t-fast)",
      display: "flex", flexDirection: "column", gap: 12,
    },
  },
    /* 헤더: 이름 + 활성 배지 */
    React.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10 } },
      React.createElement("div", { style: { minWidth: 0 } },
        React.createElement("div", { className: "t-headline-2", style: { color: "var(--label-normal)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, master.name || "제목 없는 마스터"),
        master.originalFilename ? React.createElement("div", { className: "t-caption-1", style: { color: "var(--label-alt)", marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, master.originalFilename) : null),
      active ? React.createElement(Pill, { color: "var(--primary)", bg: "var(--primary-light)" },
        React.createElement("span", { style: { width: 6, height: 6, borderRadius: "50%", background: "var(--primary)" } }), "이 차시에 적용됨") : null),

    /* 메타: 슬라이드 크기 */
    size ? React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } },
      React.createElement("span", { className: "t-caption-1", style: { color: "var(--label-alt)", fontWeight: 700 } }, "슬라이드 크기"),
      React.createElement(Pill, { color: "var(--label-neutral)", bg: "var(--bg-alt)" }, size.label),
      React.createElement("span", { className: "t-caption-1 tnum", style: { color: "var(--label-assist)" } }, `${size.wIn} × ${size.hIn} in`)) : null,

    /* 테마색 */
    React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6 } },
      React.createElement("span", { className: "t-caption-1", style: { color: "var(--label-alt)", fontWeight: 700 } }, "테마색"),
      React.createElement(ColorSwatches, { colors: master.themeColors })),

    /* 레이아웃 — 상세(GET /masters/:id) 결과가 있을 때만 */
    master.layouts !== undefined
      ? React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6 } },
          React.createElement("span", { className: "t-caption-1", style: { color: "var(--label-alt)", fontWeight: 700 } },
            "레이아웃", Array.isArray(master.layouts) && master.layouts.length ? React.createElement("span", { className: "tnum", style: { color: "var(--label-assist)", marginLeft: 5, fontWeight: 500 } }, `${master.layouts.length}종`) : null),
          React.createElement(LayoutList, { layouts: master.layouts }))
      : null,

    /* 액션 */
    (onAssign || onDelete) ? React.createElement("div", { style: { display: "flex", gap: 8, marginTop: 2, alignItems: "center" } },
      onAssign ? React.createElement(Button, {
        kind: active ? "outline" : "primary", size: "sm", disabled: busy || active,
        onClick: (e) => { e.stopPropagation(); onAssign(master); },
      }, active ? "적용됨" : "이 차시에 적용") : null,
      onDelete ? React.createElement(Button, {
        kind: "ghost", size: "sm", disabled: busy,
        onClick: (e) => { e.stopPropagation(); onDelete(master); },
        style: { color: "var(--negative)", marginLeft: "auto" },
      }, "삭제") : null) : null
  );
}

/* 업로드 드롭존 — .potx/.pptx multipart */
function MasterUploader({ onUploaded }) {
  const inputRef = useRef(null);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");
  const [name, setName] = useState("");

  async function doUpload(file) {
    if (!file) return;
    const lower = (file.name || "").toLowerCase();
    if (!/\.(potx|pptx)$/.test(lower)) {
      setErr("PowerPoint 마스터(.potx 또는 .pptx) 파일만 올릴 수 있어요.");
      return;
    }
    if (file.size > 30 * 1024 * 1024) {
      setErr("마스터 파일은 30MB 이하만 올릴 수 있어요.");
      return;
    }
    setErr(""); setBusy(true);
    try {
      const fd = new FormData();
      fd.append("file", file);                          // §3.5 field 이름 = file
      if (name.trim()) fd.append("name", name.trim());
      const { master } = await api("/masters", { method: "POST", body: fd });   // POST /api/masters
      setName("");
      onUploaded(master);
    } catch (ex) {
      setErr(ex.message || "마스터를 업로드하지 못했어요.");
    } finally {
      setBusy(false);
      if (inputRef.current) inputRef.current.value = "";
    }
  }

  return React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 10 } },
    React.createElement("input", {
      type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: "마스터 이름 (선택)",
      style: { height: 40, padding: "0 14px", borderRadius: "var(--r-sm)", border: "1px solid var(--border)", background: "var(--bg-normal)", color: "var(--label-normal)", fontSize: 14, fontFamily: "var(--font)", outline: "none" },
      onFocus: (e) => { e.target.style.borderColor = "var(--primary)"; },
      onBlur: (e) => { e.target.style.borderColor = "var(--border)"; },
    }),
    React.createElement("label", {
      style: {
        display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 6,
        padding: "26px 18px", border: "1.5px dashed var(--line-normal)", borderRadius: "var(--r-md)",
        background: "var(--bg-neutral)", cursor: busy ? "default" : "pointer", textAlign: "center",
        transition: "border-color var(--t-fast)",
      },
      onDragOver: (e) => { e.preventDefault(); e.currentTarget.style.borderColor = "var(--primary)"; },
      onDragLeave: (e) => { e.currentTarget.style.borderColor = "var(--line-normal)"; },
      onDrop: (e) => { e.preventDefault(); e.currentTarget.style.borderColor = "var(--line-normal)"; if (!busy) doUpload(e.dataTransfer?.files?.[0]); },
    },
      React.createElement("input", {
        ref: inputRef, type: "file", accept: ".potx,.pptx", disabled: busy,
        onChange: (e) => doUpload(e.target.files && e.target.files[0]),
        style: { display: "none" },
      }),
      busy
        ? React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } },
            React.createElement(Spinner, { s: 16 }),
            React.createElement("span", { className: "t-label-2", style: { color: "var(--label-neutral)" } }, "업로드하고 분석하는 중이에요"))
        : React.createElement(React.Fragment, null,
            React.createElement("svg", { width: 26, height: 26, viewBox: "0 0 24 24", fill: "none", stroke: "var(--primary)", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round" },
              React.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" })),
            React.createElement("span", { className: "t-label-1", style: { color: "var(--label-normal)" } }, "마스터 파일을 끌어다 놓거나 클릭해서 선택해요"),
            React.createElement("span", { className: "t-caption-1", style: { color: "var(--label-alt)" } }, ".potx 또는 .pptx · 30MB 이하"))
    ),
    err ? React.createElement("div", { className: "t-label-2", style: { color: "var(--negative)", background: "var(--negative-bg)", borderRadius: "var(--r-sm)", padding: "9px 12px" } }, err) : null
  );
}

/* ── 마스터 매니저 모달 ──
   목록 + 업로드 + 미리보기 + 현재 프로젝트에 적용(PATCH masterId).
   props: project(현재 차시), activeMasterId, onAssigned(master|null), onClose */
function MasterManagerModal({ project, activeMasterId, onAssigned, onClose }) {
  const [items, setItems] = useState(null);       // null=로딩 (GET /api/masters)
  const [err, setErr] = useState("");
  const [busyId, setBusyId] = useState(null);      // PATCH 진행 중 마스터 id
  const [detail, setDetail] = useState({});        // {masterId: full master w/ layouts}

  const load = useCallback(async () => {
    setErr("");
    try {
      const data = await api("/masters");          // GET /api/masters
      setItems(Array.isArray(data?.items) ? data.items : []);
    } catch (ex) {
      setErr(ex.message || "마스터 목록을 불러오지 못했어요.");
      setItems([]);
    }
  }, []);
  useEffect(() => { load(); }, [load]);

  // 상세(레이아웃 포함) 지연 로드 — GET /api/masters/:id
  const loadDetail = useCallback(async (id) => {
    if (!id || detail[id]) return;
    try {
      const { master } = await api(`/masters/${id}`);
      if (master) setDetail((d) => ({ ...d, [id]: master }));
    } catch (ex) { /* 미리보기 상세는 실패해도 목록 메타로 표시 */ }
  }, [detail]);

  async function assign(master) {
    if (!master || !project?.id) return;
    setBusyId(master.id); setErr("");
    try {
      // §3.2 PATCH masterId — 마스터 지정/교체
      const { project: updated } = await api(`/projects/${project.id}`, { method: "PATCH", body: { masterId: master.id } });
      onAssigned(master, updated);
    } catch (ex) {
      setErr(ex.message || "마스터를 적용하지 못했어요.");
    } finally { setBusyId(null); }
  }

  async function removeMaster(master) {
    if (!master) return;
    if (!window.confirm(`'${master.name || "마스터"}'를 삭제할까요? 이 마스터를 쓰던 차시는 마스터 미지정으로 바뀌어요.`)) return;
    setBusyId(master.id); setErr("");
    try {
      await api(`/masters/${master.id}`, { method: "DELETE" });   // DELETE /api/masters/:id
      if (activeMasterId === master.id) onAssigned(null, null);   // 현재 차시가 쓰던 거면 해제 반영
      load();
    } catch (ex) {
      setErr(ex.message || "마스터를 삭제하지 못했어요.");
    } finally { setBusyId(null); }
  }

  const merged = (m) => (detail[m.id] ? { ...m, ...detail[m.id] } : m);

  return React.createElement("div", {
    onClick: onClose,
    style: { position: "fixed", inset: 0, background: "rgba(20,25,30,0.46)", display: "flex", alignItems: "flex-start", justifyContent: "center", zIndex: 120, padding: "40px 24px", overflow: "auto" },
  },
    React.createElement("div", {
      onClick: (e) => e.stopPropagation(),
      style: { width: "100%", maxWidth: 720, background: "var(--bg-normal)", borderRadius: "var(--r-lg)", boxShadow: "var(--sh-3)", padding: "26px 26px 24px", display: "flex", flexDirection: "column", gap: 18 },
    },
      React.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12 } },
        React.createElement("div", null,
          React.createElement("div", { className: "t-heading-1", style: { marginBottom: 4 } }, "마스터 템플릿"),
          React.createElement("p", { className: "t-body-2", style: { color: "var(--label-alt)", margin: 0 } }, "PowerPoint 마스터를 올리면 레이아웃과 테마색을 확인하고, 이 차시에 적용해요. 나중에 마스터만 바꿔 다시 내보낼 수도 있어요.")),
        React.createElement("button", { onClick: onClose, "aria-label": "닫기", style: { ...btnStyle("ghost", "sm"), padding: "0 8px" } },
          React.createElement("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round" }, React.createElement("path", { d: "M18 6 6 18M6 6l12 12" })))),

      React.createElement(MasterUploader, { onUploaded: (m) => { setItems((arr) => [m, ...(arr || [])]); if (m) setDetail((d) => ({ ...d, [m.id]: m })); } }),

      err ? React.createElement("div", { className: "t-label-2", style: { color: "var(--negative)", background: "var(--negative-bg)", borderRadius: "var(--r-sm)", padding: "10px 14px" } }, err) : null,

      React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } },
        items === null
          ? React.createElement(StateMsg, { icon: React.createElement(Spinner, { s: 22 }), title: "마스터를 불러오는 중이에요" })
          : items.length === 0
            ? React.createElement("div", { style: { padding: "18px 0", textAlign: "center" } },
                React.createElement("span", { className: "t-body-2", style: { color: "var(--label-alt)" } }, "아직 올린 마스터가 없어요. 위에서 첫 마스터를 올려 보세요."))
            : items.map((m) => {
                const full = merged(m);
                // 상세 자동 로드(레이아웃 미리보기)
                if (full.layouts === undefined) loadDetail(m.id);
                return React.createElement(MasterPreviewCard, {
                  key: m.id, master: full, active: m.id === activeMasterId, busy: busyId === m.id,
                  onAssign: project ? assign : null,
                  onDelete: removeMaster,
                });
              })
      )
    )
  );
}

Object.assign(window, {
  MasterManagerModal, MasterPreviewCard, MasterUploader, ColorSwatches, LayoutList, emuToSize,
});
