// ── piece.jsx ──────────────────────────────────────────────────────────────
// The "tap a piece to shape it" editor (rendered inside a Sheet) + the
// redesigned custom-yarn dialog. Linking is simplified to a single "Match
// another piece" control tucked under "More", so the default view stays light.

const { useState: useStateP, useMemo: useMemoP } = React

function DimRow({ dim, range, value, locked, lockLabel, onChange }) {
  const [min, max] = range
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <span style={{ fontSize: 12.5, fontWeight: 600, color: locked ? "var(--accent)" : "var(--ink)",
          fontFamily: "var(--font-ui)", display: "flex", alignItems: "center", gap: 5 }}>
          {dim.label}{locked && <Tip text={lockLabel}><span style={{ fontSize: 11, cursor: "help", color: "var(--accent)" }}>⛓</span></Tip>}
        </span>
        <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <input type="number" min={min} max={max} value={Math.round(value)} disabled={locked}
            onChange={e => !locked && onChange(Math.min(max, Math.max(min, Number(e.target.value))))}
            style={{ width: 60, background: locked ? "var(--field-dim)" : "var(--field)",
              border: `1px solid ${locked ? "var(--line)" : "var(--line-strong)"}`, borderRadius: 8,
              padding: "6px 8px", color: locked ? "var(--muted)" : "var(--ink)", fontWeight: 700, fontSize: 13.5,
              textAlign: "right", outline: "none", fontFamily: "var(--font-ui)", fontVariantNumeric: "tabular-nums" }} />
          <span style={{ fontSize: 11, color: "var(--muted)", width: 16 }}>{dim.unit}</span>
        </div>
      </div>
      <input type="range" className="yarn-range" min={min} max={max} value={value} disabled={locked}
        onChange={e => !locked && onChange(Number(e.target.value))}
        style={{ width: "100%", opacity: locked ? .45 : 1, cursor: locked ? "not-allowed" : "pointer" }} />
    </div>
  )
}

const PSELECT = {
  background: "var(--field)", border: "1px solid var(--line-strong)", borderRadius: 10, color: "var(--ink)",
  padding: "10px 11px", fontSize: 13.5, outline: "none", fontFamily: "var(--font-ui)", cursor: "pointer", width: "100%",
}

// ── sweater neckline editor — the negative shape subtracted from the yoke ────
// The geometry engine cuts the opening from its neckline TYPE (crew / scoop / v /
// boat), so this is a type picker only.
function NeckEditor({ block, chest, onUpdate }) {
  const t = useT()
  const cur = block.neckType || "none"
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
      <FieldLabel>{t("Neckline")}</FieldLabel>
      <Segmented value={cur} size="sm" grow={false}
        onChange={(v) => onUpdate("neckType", v === "none" ? null : v)}
        options={[{ value: "none", label: t("None") }, ...Object.entries(NECK_TYPES).map(([k, v]) => ({ value: k, label: t(v.label) }))]} />
    </div>
  )
}

function PieceEditor({ block, blocks, effectiveDims, garmentYarn, meters, anchorLabel, isSized, sizeName,
                       onUpdate, onDim, onRemove, onReset, onMirrorAll, onCustomYarn, hasPos, onResetPos }) {
  const t = useT()
  const [showMore, setShowMore] = useStateP(false)
  if (!block) return null
  const prim = PRIMITIVES[block.type]
  const isOverriding = !!block.yarnOverride
  const qty = block.quantity || 1
  const hasPins = block.pinnedDims && Object.values(block.pinnedDims).some(Boolean)
  const nameOf = b => t(b.label || PRIMITIVES[b.type].label)
  const mirrorTargets = blocks.filter(b => b.id !== block.id && b.type === block.type)
  const sharedLinkTarget = (() => {
    const ids = Object.values(block.links || {})
    return ids.length && ids.every(id => id === ids[0]) ? ids[0] : null
  })()

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      {/* hero: this piece's contribution */}
      <div style={{ display: "flex", alignItems: "center", gap: 11, background: "var(--field)",
        border: "1px solid var(--line)", borderRadius: 13, padding: "11px 13px" }}>
        <span style={{ width: 12, height: 12, borderRadius: 4, background: prim.color, flexShrink: 0 }} />
        <span style={{ fontSize: 12, color: "var(--muted)", fontFamily: "var(--font-ui)", marginRight: "auto" }}>{t(prim.desc)}</span>
        <span style={{ display: "flex", alignItems: "baseline", gap: 3 }}>
          <span style={{ fontSize: 24, fontWeight: 800, color: isOverriding ? "var(--accent-2)" : "var(--accent)",
            fontVariantNumeric: "tabular-nums", fontFamily: "var(--font-ui)" }}>{meters}</span>
          <span style={{ fontSize: 12, fontWeight: 600, color: "var(--muted)" }}>m</span>
        </span>
      </div>

      {/* name */}
      <label style={{ display: "flex", flexDirection: "column", gap: 6 }}>
        <FieldLabel>{t("Name")}</FieldLabel>
        <input value={t(block.label)} placeholder={t(prim.label)} onChange={e => onUpdate("label", e.target.value)}
          style={{ ...PSELECT, fontWeight: 600 }} />
      </label>

      {/* dimensions */}
      {!prim.isFixed && (
        <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          {prim.dims.map(dim => {
            const lockedByAnchor = block.followsAnchorDim === dim.key
            const lockedByLink = !!block.links?.[dim.key]
            const locked = lockedByAnchor || lockedByLink
            const rawLabel = block.dimLabels?.[dim.key] || dim.label
            return (
              <DimRow key={dim.key} dim={{ ...dim, label: t(rawLabel) }}
                range={block.ranges[dim.key] || [dim.min, dim.max]} value={effectiveDims[dim.key]} locked={locked}
                lockLabel={lockedByAnchor ? t("Follows {x}", { x: t(anchorLabel) }) : t("Mirrored from another piece")}
                onChange={v => onDim(dim.key, v)} />
            )
          })}
        </div>
      )}

      {/* sweater neckline — a negative shape subtracted from the body */}
      {block.role === "sweater-body" && (
        <NeckEditor block={block} chest={effectiveDims.width} onUpdate={onUpdate} />
      )}

      {/* fixed overhead type */}
      {prim.isFixed && (
        <label style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          <FieldLabel>{t("Type")}</FieldLabel>
          <select value={block.fixedMeters} onChange={e => onUpdate("fixedMeters", Number(e.target.value))} style={PSELECT}>
            <option value={15}>{t("Pompom small — 15m")}</option>
            <option value={25}>{t("Pompom medium — 25m")}</option>
            <option value={40}>{t("Pompom large — 40m")}</option>
            <option value={35}>{t("Tassel — 35m")}</option>
            <option value={8}>{t("Fringe strand — 8m")}</option>
          </select>
        </label>
      )}

      {/* stitch + yarn */}
      {!prim.isFixed && (
        <label style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          <FieldLabel>{t("Stitch pattern")}</FieldLabel>
          <select value={block.construction} onChange={e => onUpdate("construction", e.target.value)} style={PSELECT}>
            {Object.entries(CONSTRUCTION).map(([k, v]) => <option key={k} value={k}>{t(v.label)} · ×{v.mod.toFixed(2)}</option>)}
          </select>
        </label>
      )}

      <label style={{ display: "flex", flexDirection: "column", gap: 6 }}>
        <FieldLabel accent={isOverriding}>{t("Yarn")} {isOverriding ? `· ${t("override")}` : `· ${t("inherits")}`}</FieldLabel>
        <select value={block.yarnOverride || ""}
          onChange={e => e.target.value === "__other" ? onCustomYarn() : onUpdate("yarnOverride", e.target.value || null)}
          style={{ ...PSELECT, borderColor: isOverriding ? "var(--accent-2)" : "var(--line-strong)",
            color: isOverriding ? "var(--accent-2)" : "var(--ink)" }}>
          <option value="">{t("Garment yarn ({x})", { x: YARN[garmentYarn].label })}</option>
          {Object.entries(YARN).map(([k, v]) => <option key={k} value={k}>{v.label} — {v.mPer100g}m/100g</option>)}
          <option value="__other">{t("Other… (custom)")}</option>
        </select>
      </label>

      {/* quantity */}
      <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
        <FieldLabel>{t("Quantity")}</FieldLabel>
        <Segmented value={qty} onChange={q => onUpdate("quantity", q)}
          options={[1, 2, 3, 4].map(q => ({ value: q, label: "×" + q }))} />
      </div>

      {/* more: match another piece */}
      {mirrorTargets.length > 0 && (
        <div>
          <button onClick={() => setShowMore(m => !m)} style={{ background: "transparent", border: "none",
            color: "var(--muted)", fontSize: 12, cursor: "pointer", fontFamily: "var(--font-ui)", padding: "2px 0",
            display: "inline-flex", alignItems: "center", gap: 5, fontWeight: 600 }}>
            <Icon.chevron s={14} style={{ transform: showMore ? "rotate(180deg)" : "none" }} /> {t("Link to another piece")}
          </button>
          {showMore && (
            <div style={{ marginTop: 8 }}>
              <select value={sharedLinkTarget || ""} onChange={e => onMirrorAll(e.target.value ? Number(e.target.value) : null)}
                style={{ ...PSELECT, borderColor: sharedLinkTarget ? "var(--accent)" : "var(--line-strong)",
                  color: sharedLinkTarget ? "var(--accent)" : "var(--ink)" }}>
                <option value="">{t("Not linked — size independently")}</option>
                {mirrorTargets.map(tb => <option key={tb.id} value={tb.id}>{t("Match {name}'s size", { name: nameOf(tb) })}</option>)}
              </select>
              <p style={{ fontSize: 11, color: "var(--muted)", lineHeight: 1.5, marginTop: 7, fontFamily: "var(--font-ui)" }}>
                {t("Linked pieces share a measurement — change one and the others follow.")}</p>
            </div>
          )}
        </div>
      )}

      {/* moved-piece note */}
      {hasPos && (
        <button onClick={onResetPos} style={{ display: "inline-flex", alignItems: "center", gap: 6, alignSelf: "flex-start",
          background: "transparent", border: "none", color: "var(--accent)", fontSize: 12, fontWeight: 600,
          cursor: "pointer", fontFamily: "var(--font-ui)", padding: "2px 0", marginTop: -4 }}>
          <Icon.reset s={13} />{t("Reset position")}</button>
      )}

      {/* actions */}
      <div style={{ display: "flex", gap: 8, marginTop: 2 }}>
        <button onClick={onReset}
          style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 6,
            background: "var(--field)", border: "1px solid var(--line-strong)", borderRadius: 11, padding: "11px 0",
            color: "var(--ink)", fontSize: 13, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font-ui)" }}>
          <Icon.reset s={15} />{isSized && block.role && hasPins ? t("Re-sync to {x}", { x: sizeName }) : t("Reset size")}</button>
        <button onClick={onRemove}
          style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 6,
            background: "transparent", border: "1px solid var(--danger-line)", borderRadius: 11, padding: "11px 16px",
            color: "var(--danger)", fontSize: 13, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font-ui)" }}>
          <Icon.trash />{t("Remove")}</button>
      </div>
    </div>
  )
}

// ── add-piece picker (inside a sheet) ───────────────────────────────────────
function AddPieceList({ onAdd }) {
  const t = useT()
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
      {Object.entries(PRIMITIVES).map(([key, p]) => (
        <button key={key} onClick={() => onAdd(key)} style={{ display: "flex", alignItems: "center", gap: 12,
          width: "100%", textAlign: "left", background: "var(--field)", border: "1px solid var(--line)",
          borderRadius: 12, padding: "12px 13px", cursor: "pointer", fontFamily: "var(--font-ui)" }}>
          <span style={{ width: 34, height: 34, borderRadius: 9, background: hexA(p.color, .15), color: p.color,
            display: "grid", placeItems: "center", fontSize: 17, flexShrink: 0 }}>{p.symbol}</span>
          <span style={{ minWidth: 0 }}>
            <span style={{ display: "block", fontSize: 14, color: "var(--ink)", fontWeight: 700 }}>{t(p.label)}</span>
            <span style={{ display: "block", fontSize: 11.5, color: "var(--muted)" }}>{t(p.desc)}</span>
          </span>
        </button>
      ))}
    </div>
  )
}

// ── custom yarn dialog (sheet) — ball band · held double · weighed swatch ────
const CY_INPUT = {
  width: "100%", background: "var(--field)", border: "1px solid var(--line-strong)", borderRadius: 10,
  padding: "11px 11px", color: "var(--ink)", fontSize: 16, fontWeight: 700, outline: "none",
  fontFamily: "var(--font-ui)", fontVariantNumeric: "tabular-nums",
}
function CYField({ label, value, set, unit, autoFocus }) {
  const t = useT()
  return (
    <label style={{ flex: 1, display: "flex", flexDirection: "column", gap: 6 }}>
      <FieldLabel>{t(label)}</FieldLabel>
      <div style={{ display: "flex", alignItems: "center", gap: 5 }}>
        <input type="number" inputMode="decimal" value={value} onChange={e => set(e.target.value)} autoFocus={autoFocus} style={CY_INPUT} />
        {unit && <span style={{ fontSize: 13, color: "var(--muted)" }}>{unit}</span>}
      </div>
    </label>
  )
}

function CustomYarnSheet({ open, onClose, onSave }) {
  const t = useT()
  const [mode, setMode] = useStateP("band")
  // band mode
  const [m, setM] = useStateP(""); const [g, setG] = useStateP("")
  const [gauge, setGauge] = useStateP(""); const [touched, setTouched] = useStateP(false)
  // held-double mode
  const [mA, setMA] = useStateP(""); const [mB, setMB] = useStateP("")
  // swatch mode
  const [sw, setSw] = useStateP(""); const [sh, setSh] = useStateP(""); const [swg, setSwg] = useStateP("")
  const [slen, setSlen] = useStateP(""); const [ssts, setSsts] = useStateP(""); const [srows, setSrows] = useStateP("")

  React.useEffect(() => { if (open) { setMode("band"); setM(""); setG(""); setGauge(""); setTouched(false);
    setMA(""); setMB(""); setSw(""); setSh(""); setSwg(""); setSlen(""); setSsts(""); setSrows("") } }, [open])

  // ── BAND ──
  const mn = parseFloat(m), gn = parseFloat(g)
  const bandValid = mn > 0 && gn > 0
  const per100 = bandValid ? Math.round(mn / gn * 100) : null
  const suggested = useMemoP(() => per100 == null ? null : stsFromMPer100g(per100), [per100])
  const gaugeVal = touched ? gauge : (suggested != null ? String(suggested) : "")
  const gaugeOut = parseFloat(gaugeVal) > 0 ? Math.round(parseFloat(gaugeVal)) : suggested

  // ── HELD DOUBLE ──
  const mAn = parseFloat(mA), mBn = parseFloat(mB)
  const dblValid = mAn > 0 && mBn > 0
  const combined = dblValid ? combineHeldDouble(mAn, mBn) : null
  const dblGauge = combined != null ? stsFromMPer100g(combined) : null

  // ── SWATCH (precision) ──
  const swn = parseFloat(sw), shn = parseFloat(sh), swgn = parseFloat(swg), slenn = parseFloat(slen)
  const area = swn > 0 && shn > 0 ? swn * shn : 0
  const swValid = area > 0 && swgn > 0 && slenn > 0
  const swMcm2 = swValid ? slenn / area : null              // metres per cm² (measured)
  const swPer100 = swValid ? Math.round(slenn / swgn * 100) : null
  const swSts = parseFloat(ssts) > 0 ? Math.round(parseFloat(ssts)) : null
  const swRows = parseFloat(srows) > 0 ? Math.round(parseFloat(srows)) : null

  const valid = mode === "band" ? bandValid : mode === "double" ? dblValid : swValid
  const save = () => {
    if (mode === "band") {
      onSave({ mPer100g: per100, gauge: gaugeOut, stdSts: gaugeOut,
        label: `${per100} m/100g`, sub: `${per100} m / 100g · ${gaugeOut} sts` })
    } else if (mode === "double") {
      // held double: mPer100g is the COMBINED strand (correct for the area path);
      // mPerStrand is the per-strand basis (= harmonic mean) used to weigh the
      // geometry engine's single-strand-metres figure. strands flags it held-double.
      onSave({ mPer100g: combined, mPerStrand: combined ? Math.round(2 * combined) : null, strands: 2,
        gauge: dblGauge, stdSts: dblGauge,
        label: t("Held double"), sub: `${mAn}+${mBn} → ${combined} m/100g` })
    } else {
      onSave({ mPer100g: swPer100, mPerCm2: swMcm2,
        gauge: swSts || stsFromMPer100g(swPer100), stdSts: swSts || stsFromMPer100g(swPer100),
        rows: swRows || null, label: t("From swatch"), sub: `${swPer100} m/100g · ${swMcm2.toFixed(3)} m/cm²` })
    }
  }

  const tab = (key, label) => (
    <button onClick={() => setMode(key)} style={{ flex: 1, border: "none", cursor: "pointer", borderRadius: 9,
      padding: "8px 6px", fontFamily: "var(--font-ui)", fontSize: 12.5, fontWeight: mode === key ? 700 : 600,
      background: mode === key ? "var(--card)" : "transparent", color: mode === key ? "var(--ink)" : "var(--muted)",
      boxShadow: mode === key ? "0 1px 3px rgba(40,30,15,.16)" : "none" }}>{label}</button>
  )

  return (
    <Sheet open={open} onClose={onClose} title={t("Your own yarn")} maxWidth={420}
      footer={
        <button disabled={!valid} onClick={() => valid && save()}
          style={{ width: "100%", background: valid ? "var(--accent)" : "var(--field-dim)",
            border: "1px solid " + (valid ? "var(--accent)" : "var(--line)"), borderRadius: 12, padding: "13px 0",
            color: valid ? "var(--on-accent)" : "var(--muted)", fontSize: 14.5, fontWeight: 700,
            cursor: valid ? "pointer" : "default", fontFamily: "var(--font-ui)" }}>{t("Use this yarn")}</button>}>
      <div style={{ display: "flex", gap: 3, background: "var(--field-dim)", borderRadius: 11, padding: 3, border: "1px solid var(--line)" }}>
        {tab("band", t("Ball band"))}{tab("double", t("Held double"))}{tab("swatch", t("Swatch"))}
      </div>

      {mode === "band" && <>
        <p style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)", margin: 0, lineHeight: 1.5 }}>
          {t("Read it off the ball band, or measure a ball you have.")}</p>
        <div style={{ display: "flex", gap: 10 }}>
          <CYField label="Length" value={m} set={setM} unit="m" autoFocus />
          <CYField label="Weight" value={g} set={setG} unit="g" />
        </div>
        <div style={{ textAlign: "center", padding: "12px 0", borderTop: "1px solid var(--line)", borderBottom: "1px solid var(--line)" }}>
          <span style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)" }}>= </span>
          <span style={{ fontSize: 26, fontWeight: 800, color: per100 ? "var(--accent)" : "var(--line-strong)",
            fontFamily: "var(--font-ui)", fontVariantNumeric: "tabular-nums" }}>{per100 != null ? per100 : "—"}</span>
          <span style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)" }}> m / 100g</span>
        </div>
        <div>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 7 }}>
            <FieldLabel>{t("Your gauge")}</FieldLabel>
            {suggested != null && (touched
              ? <button onClick={() => { setTouched(false); setGauge("") }} style={{ background: "transparent", border: "none",
                  color: "var(--accent)", cursor: "pointer", fontSize: 11, fontFamily: "var(--font-ui)", padding: 0 }}>↺ {t("we estimate {n}", { n: suggested })}</button>
              : <span style={{ fontSize: 11, color: "var(--ok)", fontFamily: "var(--font-ui)", fontWeight: 700 }}>{t("we estimate")}</span>)}
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <input type="number" inputMode="decimal" value={gaugeVal} onChange={e => { setTouched(true); setGauge(e.target.value) }}
              disabled={per100 == null} style={{ ...CY_INPUT, width: 92,
                border: `1px solid ${touched ? "var(--accent)" : "var(--line-strong)"}`, background: per100 == null ? "var(--field-dim)" : "var(--field)" }} />
            <span style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)" }}>{t("sts / 10cm")}</span>
          </div>
        </div>
      </>}

      {mode === "double" && <>
        <p style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)", margin: 0, lineHeight: 1.5 }}>
          {t("Two strands held together knit as one yarn. Enter each strand's m/100g — we combine them.")}</p>
        <div style={{ display: "flex", gap: 10 }}>
          <CYField label="Strand A" value={mA} set={setMA} unit="m/100g" autoFocus />
          <CYField label="Strand B" value={mB} set={setMB} unit="m/100g" />
        </div>
        <div style={{ textAlign: "center", padding: "12px 0", borderTop: "1px solid var(--line)", borderBottom: "1px solid var(--line)" }}>
          <span style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)" }}>{t("held together")} = </span>
          <span style={{ fontSize: 26, fontWeight: 800, color: combined ? "var(--accent)" : "var(--line-strong)",
            fontFamily: "var(--font-ui)", fontVariantNumeric: "tabular-nums" }}>{combined != null ? combined : "—"}</span>
          <span style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)" }}> m / 100g</span>
          {dblGauge != null && <div style={{ fontSize: 11.5, color: "var(--muted)", marginTop: 4 }}>{t("≈ {n} sts/10cm at the doubled gauge", { n: dblGauge })}</div>}
        </div>
      </>}

      {mode === "swatch" && <>
        <p style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)", margin: 0, lineHeight: 1.5 }}>
          {t("Most accurate. Knit a swatch, wash & block it, then measure, weigh & unravel it. Captures your real tension and fibre.")}</p>
        <div style={{ display: "flex", gap: 10 }}>
          <CYField label="Width" value={sw} set={setSw} unit="cm" autoFocus />
          <CYField label="Height" value={sh} set={setSh} unit="cm" />
        </div>
        <div style={{ display: "flex", gap: 10 }}>
          <CYField label="Weight" value={swg} set={setSwg} unit="g" />
          <CYField label="Unravelled" value={slen} set={setSlen} unit="m" />
        </div>
        <div style={{ display: "flex", gap: 10 }}>
          <CYField label="Stitches / 10cm" value={ssts} set={setSsts} />
          <CYField label="Rows / 10cm" value={srows} set={setSrows} />
        </div>
        <div style={{ textAlign: "center", padding: "12px 0", borderTop: "1px solid var(--line)", borderBottom: "1px solid var(--line)" }}>
          <span style={{ fontSize: 22, fontWeight: 800, color: swValid ? "var(--accent)" : "var(--line-strong)",
            fontFamily: "var(--font-ui)", fontVariantNumeric: "tabular-nums" }}>{swValid ? swMcm2.toFixed(3) : "—"}</span>
          <span style={{ fontSize: 13, color: "var(--muted)", fontFamily: "var(--font-ui)" }}> m/cm²{swValid ? ` · ${swPer100} m/100g` : ""}</span>
        </div>
      </>}
    </Sheet>
  )
}

Object.assign(window, { PieceEditor, AddPieceList, CustomYarnSheet })
