/* ===== admin-import-lab.jsx — import processed lab results =============
   Reads RP's own "Labor_töödeldud.xlsx", shows
   what was recognised, proposes borehole matches for confirmation, and writes a
   batch plus its rows (migrations 0010).

   The shape of this screen follows from what the real files do:
     * NOTHING IS WRITTEN UNTIL A PERSON PRESSES IMPORT. Parsing, matching and
       review all happen in the browser first.
     * MATCHES ARE PROPOSED, NEVER APPLIED BLIND — the lab's borehole numbers do
       not equal ours (bare "1" vs "PA1"), so every row shows its proposal and its
       reason, and can be overridden.
     * UNRECOGNISED COLUMNS ARE SHOWN, not swallowed. A new lab column should
       become a question rather than silently vanish.
     * Rows whose curve cannot reach 60% passing have no Cu/Cc, and that is
       reported as a count rather than hidden — it is most rows in a fine soil.
     * NO protocol-number field. RP attaches the signed protocol to the final
       report, so the number is not needed here — and one PDF carries SEVERAL of
       them (2026/136, 2026/153 …), which a single field could not represent
       honestly anyway. The column stays in the schema, unused: dropping it would
       need a migration for no gain.
*/

/* The labs RP actually uses. Free text is still allowed via "Muu…" because a new
   lab should not be blocked by a hardcoded list. */
const LAB_NAMES = [
  "TREV-2 Grupp AS",
  "Teede Tehnokeskus AS labor",
  "Eesti Keskkonnauuringute Keskuse geotehnikalabor",
];

const LAB_MATCH_LABEL = {
  unmatched: { txt: "Sidumata", cls: "red" },
  suggested: { txt: "Pakutud", cls: "amber" },
  confirmed: { txt: "Kinnitatud", cls: "green" },
};

/* Postgres type errors arrive with the useful part in .details/.hint, and showing
   only .message truncated exactly the information needed to fix it. */
function labErr(e) {
  if (!e) return "tundmatu viga";
  var parts = [e.message, e.details, e.hint].filter(Boolean);
  return parts.join(" · ").slice(0, 300);
}

/* A local list-or-other control. The field app has an equivalent in form.jsx, but
   the admin console does not load form.jsx, so referencing that component would
   have thrown the moment this screen rendered. Duplicated deliberately rather than
   moving the shared one, which would change the field app too. */
function LabPick({ value, options, onChange, placeholder }) {
  const v = value || "";
  const inList = v === "" || options.indexOf(v) !== -1;
  const [other, setOther] = useState(!inList);
  const isOther = other || !inList;
  return (
    <React.Fragment>
      <select className="select" value={isOther ? "__muu" : v}
        onChange={(e) => {
          if (e.target.value === "__muu") { setOther(true); onChange(""); }
          else { setOther(false); onChange(e.target.value); }
        }}>
        <option value="">—</option>
        {options.map((o) => <option key={o} value={o}>{o}</option>)}
        <option value="__muu">Muu…</option>
      </select>
      {isOther && (
        <input className="input" style={{ marginTop: 8 }} placeholder={placeholder || "Kirjuta ise"}
          value={v} onChange={(e) => onChange(e.target.value)} />
      )}
    </React.Fragment>
  );
}

function fmtNum(v, dp) {
  if (v === null || v === undefined || v === "") return "—";
  var n = +v;
  return isFinite(n) ? n.toFixed(dp === undefined ? 2 : dp) : String(v);
}

function LabImportScreen({ canEdit, onToast }) {
  const [pid, setPid] = useState(() => (window.readRecentProject ? readRecentProject() : "") || "");
  const [busy, setBusy] = useState("");
  const [parsed, setParsed] = useState(null);   /* { fileName, sheetNames, sheet, res } */
  const [rows, setRows] = useState([]);         /* parsed rows + match decisions */
  const [meta, setMeta] = useState({ labName: "", note: "" });
  const [pdf, setPdf] = useState(null);
  const [existing, setExisting] = useState({ batches: [], results: [] });
  const fileRef = useRef(null);
  const pdfRef = useRef(null);
  const wbRef = useRef(null);

  const proj = (window.PROJECTS || []).find((p) => p.id === pid);

  const refreshExisting = (id) => {
    if (!id) { setExisting({ batches: [], results: [] }); return; }
    RPStore.loadLabForProject(id)
      .then(setExisting)
      .catch((e) => onToast && onToast("Laboriandmete laadimine ebaõnnestus: " + e.message));
  };
  useEffect(() => { refreshExisting(pid); }, [pid]);

  /* ---------- read + parse ---------- */
  const onFile = async (e) => {
    const f = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!f) return;
    setBusy("Loen faili…"); setParsed(null); setRows([]);
    try {
      const wb = await XlsxRead.read(f);
      wbRef.current = wb;
      const first = wb.sheetNames[0];
      applySheet(wb, first, f.name);
    } catch (err) {
      onToast && onToast("Faili ei õnnestu lugeda: " + err.message);
    }
    setBusy("");
  };

  const applySheet = (wb, sheetName, fileName) => {
    const res = LabParse.parseSheet(wb.sheet(sheetName));
    setParsed({ fileName: fileName || (parsed && parsed.fileName) || "", sheetNames: wb.sheetNames, sheet: sheetName, res });
    if (!res.ok) { setRows([]); return; }
    const cands = LabParse.candidatesForProject(pid);
    setRows(res.rows.map((r) => {
      const m = LabParse.proposeMatch(r, cands);
      return Object.assign({}, r, {
        boreholeUuid: m.boreholeUuid, matchState: m.matchState,
        why: m.why, alternatives: m.alternatives || [],
      });
    }));
  };

  /* re-match when the project changes — the candidates come from it */
  useEffect(() => {
    if (wbRef.current && parsed && parsed.sheet) applySheet(wbRef.current, parsed.sheet, parsed.fileName);
  }, [pid]);

  const setRowHole = (i, uuid) => setRows((rs) => {
    const n = rs.slice();
    n[i] = Object.assign({}, n[i], {
      boreholeUuid: uuid || null,
      matchState: uuid ? "confirmed" : "unmatched",
      why: uuid ? "käsitsi valitud" : "käsitsi eemaldatud",
    });
    return n;
  });

  const confirmAllSuggested = () => setRows((rs) => rs.map((r) =>
    r.matchState === "suggested" && r.boreholeUuid
      ? Object.assign({}, r, { matchState: "confirmed" })
      : r));

  /* ---------- commit ---------- */
  const doImport = async () => {
    if (!pid) { onToast && onToast("Vali enne projekt"); return; }
    const unconfirmed = rows.filter((r) => r.matchState === "suggested").length;
    const unmatched = rows.filter((r) => !r.boreholeUuid).length;
    let warn = "Impordin " + rows.length + " rida.";
    if (unconfirmed) warn += "\n\n" + unconfirmed + " rida on veel ainult PAKUTUD (mitte kinnitatud).";
    if (unmatched) warn += "\n" + unmatched + " rida jääb puurauguga sidumata — need imporditakse, aga jäävad 'Sidumata'.";
    warn += "\n\nJätkan?";
    if (!window.confirm(warn)) return;

    setBusy("Impordin…");
    try {
      const res = await RPStore.importLabBatch(pid, {
        labName: meta.labName, note: meta.note,
        sourceFile: (parsed && parsed.fileName) || "",
      }, rows, pdf);
      onToast && onToast("Imporditud " + res.rows + " rida" + (res.pdfPath ? " + protokoll" : ""));
      setParsed(null); setRows([]); setPdf(null); wbRef.current = null;
      setMeta({ labName: "", note: "" });
      refreshExisting(pid);
    } catch (err) {
      onToast && onToast("Import ebaõnnestus: " + labErr(err));
    }
    setBusy("");
  };

  const removeBatch = async (b) => {
    if (!window.confirm("Kustutada partii" + (b.lab_name ? " (" + b.lab_name + ")" : "") +
      " ja kõik selle read?\n\nSeda ei saa tagasi võtta.")) return;
    setBusy("Kustutan…");
    try { await RPStore.deleteLabBatch(b.id); onToast && onToast("Partii kustutatud"); refreshExisting(pid); }
    catch (e) { onToast && onToast("Kustutamine ebaõnnestus: " + e.message); }
    setBusy("");
  };

  const openPdf = async (b) => {
    try {
      const url = await RPStore.labPdfUrl(b.pdf_path);
      if (url) window.open(url, "_blank", "noopener");
      else onToast && onToast("Protokolli pole lisatud");
    } catch (e) { onToast && onToast("Protokolli ei õnnestu avada"); }
  };

  const holes = ((window.BOREHOLES || {})[pid] || []);
  const res = parsed && parsed.res;
  const noCu = rows.filter((r) => r.cu === null).length;
  const counts = rows.reduce((a, r) => { a[r.matchState] = (a[r.matchState] || 0) + 1; return a; }, {});

  return (
    <div className="page-inner fade-in">
      <div className="page-head">
        <div>
          <h1 className="page-title">Laboritulemused</h1>
          <p className="page-sub">Impordi töödeldud Exceli fail · seo puuraukudega · lisa protokoll</p>
        </div>
        <div className="page-actions">
          <ProjectPicker value={pid} onChange={setPid} />
        </div>
      </div>

      {!pid && (
        <Panel title="Vali projekt">
          <p className="muted" style={{ margin: 0, fontSize: 13 }}>
            Laboritulemused kuuluvad ühe lepingu juurde. Vali ülal projekt, siis saad faili importida.
          </p>
        </Panel>
      )}

      {pid && (
        <React.Fragment>
          <Panel title="1 · Fail" sub={proj ? projLabel(proj) : ""}
            action={canEdit ? (
              <button className="btn btn-primary" onClick={() => fileRef.current && fileRef.current.click()} disabled={!!busy}>
                <Icon name="cloud" size={16} /> Vali .xlsx
              </button>
            ) : null}>
            <input ref={fileRef} type="file" accept=".xlsx" style={{ display: "none" }} onChange={onFile} />
            {!parsed && <p className="muted" style={{ margin: 0, fontSize: 13 }}>
              Vali labori töödeldud Exceli fail (nt <span className="mono">GL26016 Labor_töödeldud.xlsx</span>).
              Veerud tuvastatakse päise teksti järgi, mitte asukoha järgi — sõelakomplekt võib failide vahel erineda.
            </p>}
            {busy && <div className="gpshint ok">{busy}</div>}

            {parsed && (
              <div className="dgrid" style={{ marginTop: 4 }}>
                <div className="dr"><span className="dr-k">Fail</span><span className="dr-v mono">{parsed.fileName}</span></div>
                <div className="dr"><span className="dr-k">Leht</span><span className="dr-v">
                  {parsed.sheetNames.length > 1 ? (
                    <select className="select" value={parsed.sheet}
                      onChange={(e) => applySheet(wbRef.current, e.target.value, parsed.fileName)}>
                      {parsed.sheetNames.map((n) => <option key={n} value={n}>{n}</option>)}
                    </select>
                  ) : <span className="mono">{parsed.sheet}</span>}
                </span></div>
                <div className="dr"><span className="dr-k">Päiserida</span><span className="dr-v mono">{res.headerRow}</span></div>
                <div className="dr"><span className="dr-k">Ridu</span><span className="dr-v mono">{rows.length}</span></div>
                <div className="dr"><span className="dr-k">Sõelad</span><span className="dr-v mono">
                  {res.sieves && res.sieves.length ? res.sieves.join(" · ") + " mm" : "—"}</span></div>
                {noCu > 0 && (
                  <div className="dr"><span className="dr-k">Cu/Cc puudub</span><span className="dr-v">
                    {noCu} real — kõver ei ulatu 60% läbiminekuni. Ekstrapoleerimist ei tehta.
                  </span></div>
                )}
              </div>
            )}

            {res && !res.ok && <div className="gpshint bad" style={{ marginTop: 10 }}>{res.error}</div>}

            {res && res.unknown && res.unknown.length > 0 && (
              <div className="gpshint bad" style={{ marginTop: 10 }}>
                <b>Tundmatud veerud jäid importimata:</b>{" "}
                {res.unknown.map((u) => u.text).join(" · ")}.
                Kui neid on vaja, ütle — lisan need vastendusse.
              </div>
            )}
          </Panel>

          {rows.length > 0 && (
            <React.Fragment>
              <Panel title="2 · Seo puuraukudega"
                sub={"Kinnitatud " + (counts.confirmed || 0) + " · pakutud " + (counts.suggested || 0) + " · sidumata " + (counts.unmatched || 0)}
                action={canEdit ? (
                  <button className="btn btn-ghost" onClick={confirmAllSuggested} disabled={!counts.suggested}>
                    <Icon name="check" size={16} /> Kinnita kõik pakutud
                  </button>
                ) : null}
                bodyFlush>
                <table className="tbl mini">
                  <thead><tr>
                    <th>Rida</th><th>LAB NR</th><th>Labori auk</th><th>Proov</th><th>Intervall</th>
                    <th>Puurauk</th><th>Olek</th><th>Põhjus</th>
                  </tr></thead>
                  <tbody>
                    {rows.map((r, i) => {
                      const lbl = LAB_MATCH_LABEL[r.matchState] || LAB_MATCH_LABEL.unmatched;
                      return (
                        <tr key={i}>
                          <td className="mono">{r.sourceRow}</td>
                          <td className="mono">{r.labNr || "—"}</td>
                          <td className="mono strong">{r.labBorehole || "—"}</td>
                          <td className="mono">{r.labSampleNr || "—"}</td>
                          <td className="mono">{r.labInterval || "—"}</td>
                          <td>
                            <select className="select" value={r.boreholeUuid || ""}
                              disabled={!canEdit}
                              onChange={(e) => setRowHole(i, e.target.value)}>
                              <option value="">— sidumata —</option>
                              {holes.filter((h) => h._uuid).map((h) => <option key={h._uuid} value={h._uuid}>{h.id}</option>)}
                            </select>
                          </td>
                          <td><span className={"badge " + lbl.cls}><span className="dot" />{lbl.txt}</span></td>
                          <td className="muted" style={{ fontSize: 12 }}>{r.why || ""}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </Panel>

              <Panel title="3 · Labori andmed">
                <div className="dgrid">
                  <div className="field"><label>Labor</label>
                    <LabPick value={meta.labName} options={LAB_NAMES}
                      onChange={(v) => setMeta({ ...meta, labName: v })} placeholder="muu labor" />
                  </div>
                  <div className="field" style={{ gridColumn: "1/-1" }}><label>Märkus</label>
                    <input className="input" value={meta.note}
                      onChange={(e) => setMeta({ ...meta, note: e.target.value })} /></div>
                </div>
                <div className="gpsrow" style={{ marginTop: 12 }}>
                  <button className="btn btn-ghost" onClick={() => pdfRef.current && pdfRef.current.click()}>
                    <Icon name="archive" size={16} /> {pdf ? "Vaheta protokoll" : "Lisa allkirjastatud protokoll (PDF)"}
                  </button>
                  <input ref={pdfRef} type="file" accept="application/pdf" style={{ display: "none" }}
                    onChange={(e) => { const f = e.target.files && e.target.files[0]; e.target.value = ""; if (f) setPdf(f); }} />
                  {pdf && <span className="mono" style={{ fontSize: 12 }}>{pdf.name} ({Math.round(pdf.size / 1024)} KB)</span>}
                </div>
                <p className="muted" style={{ fontSize: 12, marginTop: 10, marginBottom: 0 }}>
                  Protokoll salvestatakse nimega <span className="mono">{(proj ? proj.code + (proj.part ? "-" + proj.part : "") : "GL…")}_Teimiprotokoll.pdf</span>.
                </p>
              </Panel>

              <div className="page-actions" style={{ justifyContent: "flex-end", marginBottom: 20 }}>
                <button className="btn btn-ghost" onClick={() => { setParsed(null); setRows([]); setPdf(null); wbRef.current = null; }}>
                  Loobu
                </button>
                <button className="btn btn-primary gate-edit" onClick={doImport} disabled={!!busy || !canEdit}>
                  <Icon name="save" size={16} /> Impordi {rows.length} rida
                </button>
              </div>
            </React.Fragment>
          )}

          <Panel title="Selle projekti laboripartiid"
            sub={existing.batches.length + " partii · " + existing.results.length + " rida"} bodyFlush>
            {existing.batches.length === 0 ? (
              <div className="panel-body"><p className="muted" style={{ margin: 0, fontSize: 13 }}>
                Ühtegi laboripartiid pole veel imporditud.</p></div>
            ) : (
              <table className="tbl mini">
                <thead><tr><th>Imporditud</th><th>Labor</th><th>Fail</th><th>Ridu</th><th></th></tr></thead>
                <tbody>
                  {existing.batches.map((b) => {
                    const n = existing.results.filter((r) => r.batch_id === b.id).length;
                    return (
                      <tr key={b.id}>
                        <td className="mono">{(b.imported_at || "").slice(0, 10)}</td>
                        <td>{b.lab_name || "—"}</td>
                        <td className="mono" style={{ fontSize: 11 }}>{b.source_file || "—"}</td>
                        <td className="num mono">{n}</td>
                        <td style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                          {b.pdf_path && (
                            <button className="iconbtn" title="Ava protokoll" onClick={() => openPdf(b)}>
                              <Icon name="archive" size={16} />
                            </button>
                          )}
                          {canEdit && (
                            <button className="iconbtn gate-edit" title="Kustuta partii" onClick={() => removeBatch(b)}>
                              <Icon name="trash" size={16} />
                            </button>
                          )}
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            )}
          </Panel>

          {existing.results.length > 0 && (
            <Panel title="Imporditud tulemused" sub="Cu/Cc ja d-väärtused on arvutatud sõelakõverast, mitte imporditud" bodyFlush>
              <table className="tbl mini">
                <thead><tr>
                  <th>Labori auk</th><th>Proov</th><th>Intervall</th><th>Peenosised %</th>
                  <th>Liiv %</th><th>Kruus %</th><th>WL</th><th>IP</th><th>k m/ööp</th>
                  <th>Cu</th><th>Cc</th><th>ISO</th>
                </tr></thead>
                <tbody>
                  {existing.results.slice(0, 200).map((r) => (
                    <tr key={r.id}>
                      <td className="mono strong">{r.lab_borehole || "—"}</td>
                      <td className="mono">{r.lab_sample_nr || "—"}</td>
                      <td className="mono">{r.lab_interval || "—"}</td>
                      <td className="num mono">{fmtNum(r.fines_pct, 1)}</td>
                      <td className="num mono">{fmtNum(r.sand_pct, 1)}</td>
                      <td className="num mono">{fmtNum(r.gravel_pct, 1)}</td>
                      <td className="num mono">{fmtNum(r.w_liquid, 1)}</td>
                      <td className="num mono">{fmtNum(r.plasticity_ix, 1)}</td>
                      <td className="num mono">{r.permeability_text || fmtNum(r.permeability, 2)}</td>
                      <td className="num mono">{fmtNum(r.cu, 2)}</td>
                      <td className="num mono">{fmtNum(r.cc, 2)}</td>
                      <td className="mono">{r.iso_class || "—"}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
              {existing.results.length > 200 && (
                <div className="panel-body"><p className="muted" style={{ margin: 0, fontSize: 12 }}>
                  Kuvatud esimesed 200 rida {existing.results.length}-st.</p></div>
              )}
            </Panel>
          )}
        </React.Fragment>
      )}
    </div>
  );
}

Object.assign(window, { LabImportScreen });
