/* blueyetutor — /upload
 * Subject family (Physics | Calculus BC[준비 중]) + AI model + multi-PDF.
 * After 처리 시작: classifying → running → done, with cancel mid-flight.
 * Result panel: per-subject group cards w/ category_counts metrics +
 * MCQ/FRQ tabs of cropped problems.
 *
 * Assumed endpoints (mock now):
 *   POST   /api/uploads { subject_family, label_model, files[multipart] } → job
 *   GET    /api/uploads/{id} → job (poll)
 *   POST   /api/uploads/{id}/cancel
 */
const { useState: upUseState, useEffect: upUseEffect, useMemo: upUseMemo } = React;

function UploadPage() {
  const store = useStore();
  const job = store.job;
  const [family, setFamily] = upUseState("Physics");
  const [model, setModel] = upUseState(LABEL_MODELS[0].id);
  const [files, setFiles] = upUseState([]);
  // 처리 경로는 항상 자동: 백엔드(wrapper.py)가 텍스트 레이어 + CB 형식을
  // 보고 결정론 / AI vision 중 적절한 쪽으로 라우팅한다. UI 토글 없음.
  const running = job && job.status !== "done" && job.status !== "error" && job.status !== "cancelled";
  const fileInput = React.useRef(null);

  // 박현우 명시 2026-05-21: 파일 선택 누적 (이전 선택 유지) + 합계 용량 상한.
  // 학원이 큰 batch 올리면 OCR + Anthropic API 비용·시간 폭증해서 상한 필수.
  // 1 권 시험지 ≈ 30-50MB → 500MB 면 10-15개 동시 (한 batch 의 합리적 상한).
  const MAX_TOTAL_MB = 500;
  const onPick = e => {
    const incoming = Array.from(e.target.files || []);
    // 중복 제거 (이름+사이즈 기준).
    const seen = new Set(files.map(f => f.name + "|" + f.size));
    const fresh = incoming.filter(f => !seen.has(f.name + "|" + f.size));
    const next = [...files, ...fresh];
    const totalMB = next.reduce((s, f) => s + f.size, 0) / (1024 * 1024);
    if (totalMB > MAX_TOTAL_MB) {
      alert(`전체 용량이 ${MAX_TOTAL_MB}MB 를 넘어요 (현재 ${totalMB.toFixed(1)}MB). ` +
            `한 번에 너무 많이 올리면 처리 시간·비용이 폭증합니다. 일부 파일을 빼고 다시 선택해 주세요.`);
      if (e.target) e.target.value = "";
      return;
    }
    setFiles(next);
    if (e.target) e.target.value = "";   // 같은 파일 재선택도 동작하게.
  };
  const removeFile = (idx) => setFiles(files.filter((_, i) => i !== idx));
  const clearFiles = () => setFiles([]);

  const start = () => {
    if (files.length === 0) return;
    window.storeActions.startUpload(files, model, family);
  };

  return (
    <div className="page col" style={{ gap: 24 }}>
      <div className="page-header">
        <div className="eyebrow">Upload</div>
        <h1>PDF를 올려 문제를 채워 주세요</h1>
      </div>

      <div className="row" style={{ gap: 24, alignItems: "flex-start", flexWrap: "wrap" }}>
        {/* ---- 좌측 col: 처리 설정 + Job Panel (처리 중 / 결과 직후) ----
            박현우 명시 2026-05-21: Job Panel 이 우측 "지원 과목" 카드 아래에
            뜨던 옛 layout 을 처리설정 바로 아래로 이동 — 사용자가 같은
            시야에 처리 상황을 보게. */}
        <div className="col grow" style={{ gap: 16 }}>
        <Card title="처리 설정">
          <div className="col" style={{ gap: 16 }}>
            <Field label="AI 라벨링">
              <div className="row" style={{ gap: 6, alignItems: "center" }}>
                <Badge kind="navy">Claude</Badge>
              </div>
            </Field>

            <Field label={`PDF 파일 (여러 개 가능 — 합계 최대 ${MAX_TOTAL_MB}MB)`}>
              <div className="row" style={{ gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                <Button onClick={() => fileInput.current && fileInput.current.click()}
                        disabled={running}>
                  파일 추가
                </Button>
                {files.length > 0 && (
                  <>
                    <span className="muted small">
                      {files.length}개 선택됨 ·{" "}
                      {(files.reduce((s, f) => s + f.size, 0) / 1024 / 1024).toFixed(1)} / {MAX_TOTAL_MB} MB
                    </span>
                    <Button size="sm" variant="ghost" onClick={clearFiles} disabled={running}>
                      전체 해제
                    </Button>
                  </>
                )}
                <input
                  ref={fileInput} type="file" accept="application/pdf" multiple
                  style={{ display: "none" }} onChange={onPick}
                />
              </div>
              {files.length > 0 && (
                <div className="col" style={{ gap: 4, marginTop: 8 }}>
                  {files.map((f, i) => (
                    <div key={f.name + "|" + f.size + "|" + i}
                         className="row" style={{ gap: 8, alignItems: "center" }}>
                      <Icon.File />
                      <span className="mono small" style={{ flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{f.name}</span>
                      <span className="muted small">{(f.size / 1024 / 1024).toFixed(1)} MB</span>
                      <button type="button" className="icon-btn" title="이 파일 제거"
                              onClick={() => removeFile(i)} disabled={running}>
                        <Icon.Close />
                      </button>
                    </div>
                  ))}
                </div>
              )}
            </Field>

            <div className="cluster">
              <Button variant="primary" size="lg" onClick={start}
                      disabled={files.length === 0 || running}>
                처리 시작
              </Button>
              {job && !running && (
                <Button variant="ghost" onClick={() => window.storeActions.clearJob()}>이전 결과 지우고 초기화</Button>
              )}
            </div>
          </div>
        </Card>

        {/* ---- Side panel: supported subjects catalog ----
            처리 가능한 과목 + 향후 합류 예정 과목들을 한눈에. 활성 6 과목만
            클릭 가능 (현재는 자동 분류라 실제 동작은 없고 시각적 강조용),
            나머지는 disabled + 작은 '준비 중'. */}
          {/* Job Panel — 처리 설정 바로 아래 (좌측 col 안). */}
          {job && <UploadJobPanel job={job} />}
        </div>

        <div style={{ width: 320, maxWidth: "100%" }}>
          <Card title="지원 과목">
            <SupportedSubjects />
          </Card>
        </div>
      </div>
    </div>
  );
}

// 학원이 보기 좋은 한 줄 단계 메시지. 마지막 의미있는 로그를 보고 단계명을
// 결정한다. 검정 터미널 출력은 details 안으로 숨김 (혹시 디버그 필요할 때만).
function _stageMessage(log) {
  const lines = (log || []).slice().reverse();
  for (const raw of lines) {
    const l = (raw || "").trim();
    if (!l) continue;
    // 가장 구체적인 키워드부터 매칭
    if (/persist|web\.db|Library/i.test(l))      return "최종 저장 중입니다";
    if (/라벨링|labeling|RULE|AI/i.test(l) && /라벨/i.test(l))
                                                  return "AI 가 단원·답을 라벨링 중입니다";
    if (/정답지|answer_key|answers/i.test(l))    return "정답지 처리 중입니다";
    if (/crop|분할|자르/i.test(l))               return "문제별로 자르는 중입니다";
    if (/cache 합성|text_class|blocks\.json/i.test(l))
                                                  return "구조 분석 중입니다";
    if (/VLM|vision|p\d{3}/i.test(l))            return "AI 가 이미지 분석 중입니다";
    if (/렌더|render/i.test(l))                  return "페이지 이미지 만드는 중입니다";
    if (/OCR|글자 인식/i.test(l))                return "글자 인식 중입니다";
    if (/분류 중:|분류 완료|연도|과목/i.test(l)) return "과목·연도 분류 중입니다";
    if (/합치는|combine/i.test(l))               return "PDF 합치는 중입니다";
    if (/^\s*\d+\/\d+\s+(.+)/.test(l)) {
      const m = l.match(/^\s*\d+\/\d+\s+(.+)/);
      return (m && m[1].slice(0, 60)) || "처리 중입니다";
    }
    return "처리 중입니다";
  }
  return "준비 중입니다";
}

function LogTail({ log }) {
  const lines = log || [];
  if (!lines.length) return null;
  return (
    <details style={{ marginTop: 4 }}>
      <summary className="muted small" style={{ cursor: "pointer" }}>
        자세한 처리 로그 보기 ({lines.length}줄)
      </summary>
      <pre style={{
        marginTop: 6, padding: "8px 10px", borderRadius: 6,
        background: "var(--bg-1, #f8f8fa)", color: "var(--text-2)",
        fontSize: 11, lineHeight: 1.4, maxHeight: 260, overflow: "auto",
        whiteSpace: "pre-wrap", wordBreak: "break-word",
      }}>{lines.slice(-300).join("\n")}</pre>
    </details>
  );
}

function UploadJobPanel({ job }) {
  const [, setTick] = upUseState(0);
  upUseEffect(() => {
    if (job.status !== "running" && job.status !== "classifying") return;
    const t = setInterval(() => setTick(n => n + 1), 1000);
    return () => clearInterval(t);
  }, [job.status]);

  const elapsed = Math.max(0, Math.floor(Date.now() / 1000 - job.started_at));
  const mm = String(Math.floor(elapsed / 60)).padStart(2, "0");
  const ss = String(elapsed % 60).padStart(2, "0");
  const gd = job.groups_done || 0, gt = job.groups_total || 0;
  const running = job.status === "classifying" || job.status === "running";

  return (
    <div className="col" style={{ gap: 16 }}>
      {running && (
        <Card title="처리 중"
              actions={<Button variant="danger" size="sm" onClick={() => window.storeActions.cancelUpload()}><Icon.Stop /> 중지</Button>}>
          <div className="col" style={{ gap: 12 }}>
            <div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
              <div>
                <div style={{ fontWeight: 600, fontSize: 16 }}>
                  {_stageMessage(job.log)}
                </div>
                <div className="muted small">
                  {job.status === "classifying"
                    ? `과목·연도 분류 단계 · 경과 ${mm}:${ss}`
                    : `파일 ${gd}/${gt || "?"} 완료 · 경과 ${mm}:${ss}`}
                  {" · "}끝나는 파일부터 바로 Library 에 추가됩니다
                </div>
              </div>
              {job.cancel_requested && <Badge kind="warn">중지 요청됨</Badge>}
            </div>
            <Progress value={gt ? (gd / gt) * 100 : 0} indeterminate={!gt} />
            <LogTail log={job.log} />
          </div>
        </Card>
      )}

      {job.status === "cancelled" && (
        <Banner kind="warn" title="처리가 중단되었습니다">
          이미 끝난 파일은 Library 에 남아 있어요. 아래·Library 에서 확인하세요.
        </Banner>
      )}
      {job.status === "error" && (
        <Banner kind="error" title="처리 실패">{job.error || "원인 미상"}</Banner>
      )}

      {(job.results || []).length > 0 && <UploadResults job={job} />}
    </div>
  );
}

function UploadResults({ job }) {
  const results = job.results || [];
  if (results.length === 0) return null;
  const isBad = r => r.status === "failed" || r.status === "unsupported"
    || !((r.result && r.result.problems) || []).length;
  const bad = results.filter(isBad).length;
  const ok = results.length - bad;
  const done = job.status === "done" || job.status === "cancelled";
  return (
    <div className="col" style={{ gap: 16 }}>
      {done && (bad > 0
        ? <Banner kind={ok > 0 ? "warn" : "error"} title={`성공 ${ok} · 실패/미지원 ${bad}`}>
            성공한 파일은 Library 에 추가됐어요. 실패·미지원 파일은 아래에 따로 표시됩니다.
          </Banner>
        : <Banner kind="ok" title="처리 완료">
            업로드한 자료가 모두 Library 에 추가됐어요.
          </Banner>)}
      {results.map((r, i) => <UploadOneResult key={i} item={r} />)}
    </div>
  );
}

function UploadOneResult({ item }) {
  const [tab, setTab] = upUseState("MCQ");
  const r = item.result || {};
  const files = (item.files || []).join(", ");
  const mcq = (r.problems || []).filter(p => p.qtype === "MCQ");
  const frq = (r.problems || []).filter(p => p.qtype === "FRQ");
  const total = mcq.length + frq.length;
  const labeled = r.labeled_count || 0;
  const failed = item.status === "failed" || item.status === "unsupported" || total === 0;
  const titleName = files || r.pdf_id || "파일";

  if (failed) {
    return (
      <Card title={`${item.subject || "?"} — ${titleName}`}
            actions={<Badge kind="warn">지원 안 함</Badge>}>
        <Banner kind="error" title="이 파일은 지원하지 않습니다">
          {item.status === "failed"
            ? <>처리 중 오류로 건너뛰었습니다{item.error ? <> — <span className="mono small">{item.error}</span></> : null}. 다른 파일 처리에는 영향이 없습니다.</>
            : <>College Board 공식 형식이 아닐 수 있어요 (학원·출판사 자료 / 한글 혼재 / 스캔 품질). collegeboard.org 원본 PDF 로 다시 시도하세요.</>}
        </Banner>
      </Card>
    );
  }

  return (
    <Card title={`${item.subject} — ${r.pdf_id}`}
          actions={files ? <span className="muted small mono">{files}</span> : null}>
      <div className="col" style={{ gap: 16 }}>
        <div className="metric-grid">
          <Metric label="총 페이지" value={r.page_count} />
          <Metric label="MCQ" value={mcq.length} highlight />
          <Metric label="FRQ" value={frq.length} highlight />
          <Metric label="라벨링 완료" value={`${labeled} / ${total}`} />
        </div>

        {total > 0 && labeled < total && (
          <Banner kind="warn" title="AI 라벨링 일부 누락">
            동일 PDF 를 다시 처리하면 누락분만 resumable 로 채워집니다.
          </Banner>
        )}

        <details>
          <summary className="muted small">페이지 분류 통계</summary>
          <div className="row" style={{ gap: 8, marginTop: 8, flexWrap: "wrap" }}>
            {Object.entries(r.category_counts || {}).map(([k, v]) =>
              <Badge key={k} mono>{k} × {v}</Badge>)}
          </div>
        </details>

        <Tabs
          tabs={[{ value: "MCQ", label: `MCQ (${mcq.length})` }, { value: "FRQ", label: `FRQ (${frq.length})` }]}
          value={tab} onChange={setTab}
        />
        {tab === "MCQ" ? (
          <div className="problem-grid">
            {mcq.slice(0, 12).map(p => <ProblemCard key={p.id} problem={p} />)}
          </div>
        ) : (
          <div className="problem-grid frq">
            {frq.slice(0, 4).map(p => (
              <ProblemCard key={p.id} problem={p} scoringPaths={p.scoring_paths} />
            ))}
          </div>
        )}
        <div className="muted small">표시는 12개까지 · 전체 확인은 Library 에서.</div>
      </div>
    </Card>
  );
}

function SupportedSubjects() {
  return (
    <div className="col" style={{ gap: 14 }}>
      {Object.keys(PROGRAMS).map(prog => {
        const subs = PROGRAMS[prog].subjects;
        return (
          <div key={prog} className="col" style={{ gap: 6 }}>
            <div className="muted small" style={{ fontWeight: 600 }}>{prog}</div>
            <div className="cluster" style={{ rowGap: 6 }}>
              {subs.map(s => {
                const ready = READY_SUBJECTS.has(s);
                return (
                  <button key={s}
                          className={"chip " + (ready ? "active" : "")}
                          disabled={!ready}
                          style={!ready ? { opacity: 0.45, cursor: "not-allowed" } : undefined}
                          title={ready ? s : `${s} — 준비 중`}>
                    {s}
                    {!ready && <span className="muted" style={{ marginLeft: 6, fontSize: 10 }}>준비 중</span>}
                  </button>
                );
              })}
            </div>
          </div>
        );
      })}
    </div>
  );
}

Object.assign(window, { UploadPage });
