/* ===== admin-layer-naming.jsx — office naming for one geology layer =========
   Migration 0012.

   TWO VOCABULARIES, on purpose. The field crew logs a FIELD MATERIAL ("liivane
   savimoreen") because that is what you can tell with a hand lens in a trench.
   Reports and analysis use the lab's ISO name qualified by the soil group
   ("C - saCl"). Neither replaces the other, so a layer carries both, and this is
   where the office sets the second one.

   AUTO-FILL RULE (Christopher, 20.08): apply the lab's value silently when every
   lab sample in the layer agrees; when they disagree, leave it blank and show
   what was actually measured for a person to choose. A report must never carry a
   name the app picked between two contradictory samples.

   Pinnasegrupp is OPTIONAL by instruction — coarse soils do not get one — so a
   blank group means "not applicable", never "missing", and is never flagged.

   Writes go through RPStore.setLayerFields(), a targeted UPDATE on the geology
   row. Deliberately NOT a save_borehole_log() round trip: these are one-field
   office edits, and the RPC would need the whole log plus the collision guard.
*/

function LayerNaming({ bhId, idx, g, lab, onToast }) {
  const rows = (lab && lab.rows) || [];
  const prop = LabParse.proposeLayerIso(rows);
  const moist = LabParse.moisturesInLayer(rows, g.top, g.base);
  const wRes = LabParse.resolveLayerW(g.wPick, moist, g.top, g.base);

  /* local drafts so typing is not a write per keystroke; committed on blur */
  const [isoDraft, setIsoDraft] = useState(g.isoClass || "");
  const [grpDraft, setGrpDraft] = useState(g.soilGroup || "");
  const [agsDraft, setAgsDraft] = useState(g.agsName || "");
  useEffect(() => { setIsoDraft(g.isoClass || ""); }, [g.isoClass]);
  useEffect(() => { setGrpDraft(g.soilGroup || ""); }, [g.soilGroup]);
  useEffect(() => { setAgsDraft(g.agsName || ""); }, [g.agsName]);

  const put = (patch) => RPStore.setLayerFields(bhId, idx, patch)
    .catch((e) => onToast && onToast("Ei saanud salvestada: " + (e && e.message ? e.message : e), true));

  /* Auto-apply only while the layer is UNTOUCHED (iso_source ''). Guarding on the
     values themselves does not work: clearing a value makes the field empty again,
     the lab still agrees, and the fill instantly undoes the clear - so a wrongly
     assigned soil group on a coarse soil could never be removed. Any manual edit,
     including clearing, stamps 'manual' and ends auto-fill for that layer. */
  const untouched = !(g.isoSource || "");
  const autoIso = !!(untouched && prop.iso.agree && prop.iso.value);
  const autoGrp = !!(untouched && prop.group.agree && prop.group.value);
  useEffect(() => {
    if (!autoIso && !autoGrp) return;
    const patch = { isoSource: "lab" };
    if (autoIso) {
      patch.isoClass = prop.iso.value;
      /* THE AGS NAME IS THE ISO NAME (0013). Filled together with it rather than
         mapped through a table - the office wants a layer called orFSaP to carry
         that as its AGS name, not a KAP code looked up from it. Only when the AGS
         name is still blank, so this never overwrites a typed one. */
      if (!(g.agsName || "").trim()) patch.agsName = prop.iso.value;
    }
    if (autoGrp) patch.soilGroup = prop.group.value;
    put(patch);
  }, [autoIso, autoGrp, prop.iso.value, prop.group.value]);

  /* Organic content, taken from the lab and STORED on the layer so the colour
     resolves in the fence view and in exports, neither of which loads lab
     results. Written once when it changes; null when nothing was tested, which is
     not the same as 0%. */
  const labOrg = LabParse.layerOrganicPct(rows);
  const storedOrg = (g.organicPct === "" || g.organicPct === undefined || g.organicPct === null)
    ? null : parseFloat(String(g.organicPct).replace(",", "."));
  useEffect(() => {
    if (labOrg === null) return;
    if (storedOrg !== null && Math.abs(storedOrg - labOrg) < 0.005) return;
    put({ organicPct: String(labOrg) });
  }, [labOrg, storedOrg]);

  const isoConflict = prop.iso.options.length > 1;
  const grpConflict = prop.group.options.length > 1;
  const hex = layerHex(g);
  const wAuto = moist.length ? LabParse.resolveLayerW("", moist, g.top, g.base) : null;
  const wAvg = moist.length > 1 ? LabParse.resolveLayerW("avg", moist, g.top, g.base) : null;

  return (
    <div className="lyrname">
      <div className="lyrname-row">
        <label className="lyrname-f">
          <span className="lyrname-k">ISO nimi</span>
          <input className="input mono sm" value={isoDraft} placeholder="nt saCl"
            onChange={(e) => setIsoDraft(e.target.value)}
            onBlur={() => { if (isoDraft.trim() !== (g.isoClass || "")) put({ isoClass: isoDraft.trim(), isoSource: "manual" }); }} />
        </label>
        <label className="lyrname-f">
          {/* Free text on purpose: the ISO vocabulary is open, so no fixed list can
              hold every name a lab will write. geology.mat stays the KAP class. */}
          <span className="lyrname-k">AGS nimi</span>
          <input className="input mono sm" value={agsDraft}
            placeholder={(g.isoClass || "").trim() || "nt orFSaP"}
            onChange={(e) => setAgsDraft(e.target.value)}
            onBlur={() => { if (agsDraft.trim() !== (g.agsName || "")) put({ agsName: agsDraft.trim() }); }} />
        </label>
        <label className="lyrname-f narrow">
          <span className="lyrname-k">Pinnasegrupp</span>
          <input className="input mono sm" value={grpDraft} placeholder="—"
            onChange={(e) => setGrpDraft(e.target.value)}
            onBlur={() => { if (grpDraft.trim() !== (g.soilGroup || "")) put({ soilGroup: grpDraft.trim(), isoSource: "manual" }); }} />
        </label>
        <label className="lyrname-f swatch"
          title={g.hex ? "Kihi oma värv — × taastab päritud värvi"
                       : "Päritud ISO klassist, muidu välimaterjalist"}>
          <span className="lyrname-k">Värv</span>
          <span className="lyrname-cwrap">
            <input type="color" value={/^#[0-9a-f]{6}$/i.test(hex) ? hex : "#888888"}
              onChange={(e) => put({ hex: e.target.value })} />
            {g.hex && <button type="button" className="lyrname-clear"
              title="Kasuta päritud värvi" onClick={() => put({ hex: "" })}>×</button>}
          </span>
        </label>
      </div>

      {(isoConflict || grpConflict) && (
        <div className="lyrname-warn">
          {isoConflict && (
            <span>Labor annab sellele kihile mitu ISO nime:{" "}
              {prop.iso.options.map((v) => (
                <button key={v} type="button" className="lyrname-opt"
                  onClick={() => put({ isoClass: v, isoSource: "lab" })}>{v}</button>
              ))} — vali üks.{" "}
            </span>
          )}
          {grpConflict && (
            <span>Pinnasegrupid erinevad:{" "}
              {prop.group.options.map((v) => (
                <button key={v} type="button" className="lyrname-opt"
                  onClick={() => put({ soilGroup: v, isoSource: "lab" })}>{v}</button>
              ))}.
            </span>
          )}
        </div>
      )}

      {/* Why this layer is red. Worth stating: red normally means group D, and an
          organic soil borrowing that colour would otherwise look like a mistake. */}
      {layerIsOrganic(g) && (
        <div className="lyrname-hint org">
          Orgaaniline{storedOrg !== null ? " — " + storedOrg.toFixed(1) + "% orgaanikat" : ""}
          {" — värv nagu grupp D"}
        </div>
      )}

      {/* The lab says organic but the ISO name does not. Not corrected silently:
          only the office knows the right name, and 'or' is a naming decision. */}
      {storedOrg !== null && storedOrg > ORGANIC_PCT_MIN
        && (g.isoClass || "").trim() && !/^or/.test((g.isoClass || "").trim()) && (
        <div className="lyrname-warn">
          Labor: {storedOrg.toFixed(1)}% orgaanikat, aga ISO nimi{" "}
          <b>{g.isoClass}</b> ei alga <b>or</b>-iga — kontrolli nime.
        </div>
      )}

      {/* a manual override is worth seeing: a report reader may want to know the
          name was typed rather than measured */}
      {(g.isoSource || "") === "manual" && (g.isoClass || g.soilGroup) && (
        <div className="lyrname-hint">Käsitsi määratud</div>
      )}

      {/* the lab agrees but the layer says something else — offer, never impose */}
      {!isoConflict && prop.iso.value && (g.isoClass || "") !== prop.iso.value && (
        <div className="lyrname-hint">
          Labor pakub:{" "}
          <button type="button" className="lyrname-opt"
            onClick={() => put({ isoClass: prop.iso.value, isoSource: "lab" })}>{prop.iso.value}</button>
        </div>
      )}

      {/* ---- which measured moisture represents this layer ----
          Defaults to the measurement nearest the layer's centre. "Keskmine" only
          appears when there is more than one to average. */}
      {moist.length > 0 && (
        <div className="lyrname-row">
          <label className="lyrname-f wide">
            <span className="lyrname-k">
              Niiskus w
              {wRes.stale && <span className="lyrname-stale"> · salvestatud sügavust pole enam</span>}
            </span>
            <select className="select sm" value={g.wPick || ""}
              onChange={(e) => put({ wPick: e.target.value })}>
              <option value="">
                {"Lähim keskele — " + wAuto.w.toFixed(1) + "%"}
              </option>
              {wAvg && <option value="avg">
                {"Keskmine " + (wAvg.avgOverPoints ? "niiskusproovidest " : "") +
                  "(" + wAvg.n + ") — " + wAvg.w.toFixed(1) + "%"}
              </option>}
              {moist.map((mm) => (
                <option key={mm.id} value={LabParse.wDepthKey(mm)}>
                  {(mm.single ? mm.depth.toFixed(2) + " m" : (mm.interval || mm.depth.toFixed(2) + " m")) +
                    (mm.sampleNr ? " (pr " + mm.sampleNr + ")" : "") +
                    " — " + mm.w.toFixed(1) + "%"}
                </option>
              ))}
            </select>
          </label>
          <div className="lyrname-wval mono">
            {wRes.w === null ? "—" : wRes.w.toFixed(1) + "%"}
            <span className="lyrname-wsub">
              {wRes.mode === "avg" ? "keskmine" : wRes.label}
            </span>
          </div>
        </div>
      )}
    </div>
  );
}

Object.assign(window, { LayerNaming });
