// hydor-maintenance.jsx — the maintenance-contract screens (owner-approved 2026-09-28).
//
// Everything a person does with a maintenance contract, in one place:
//   ContractActivationForm   "Activate contract and record first visit" — paid in full, signed, >= 75.000 JOD
//   ContractVisitForm        "Complete maintenance visit" — the next unused visit, 0 JOD charged
//   ContractSummary          status, dates, visits used / remaining, next contact, the signed contract
//   ContractFollowupSection  the Call Centre's "Maintenance contracts requiring follow-up"
//   ContractReviewSection    the owner's review of legacy contracts, and the audited corrections
// Every change goes through Hydor.performMaintenanceAction — the server's maintenanceContract
// operation — and nothing is shown as done before its answer. Costs and review flags are rendered
// only for the roles that may read them (founders; the owner for review and corrections).

const mcT = (x) => ((window.__t || ((y) => y))(x));
const mcMC = () => window.HydorMaintenanceContracts || null;

// One contract action at a time, with its exact reason when nothing changed. The engine is captured
// at render, so an answer that arrives later is applied to the device that asked.
function useContractAction() {
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState(null);
  const inFlight = React.useRef(false);
  const alive = React.useRef(true);
  React.useEffect(() => () => { alive.current = false; }, []);
  const H = Hydor;
  const run = async (action, input) => {
    if (inFlight.current) return { ok: false, busy: true };
    inFlight.current = true;
    if (alive.current) { setBusy(true); setError(null); }
    let r;
    try { r = await H.performMaintenanceAction(action, input); }
    catch (e) { r = { ok: false, reason: (e && e.message) || String(e) }; }
    inFlight.current = false;
    // An unanswered action is NOT called a failure either: it may have been recorded — pressing again
    // replays the same operation and says which.
    if (alive.current) { setBusy(false); if (!r.ok) setError({ message: r.reason || "Not saved — nothing was changed.", uncertain: !!r.uncertain }); }
    return r;
  };
  return { run, busy, error, clear: () => setError(null) };
}

function ContractError({ error }) {
  if (!error || !error.message) return null;
  return (
    <div role="alert" data-testid="contract-error" style={{ color: error.uncertain ? "var(--warn)" : "var(--neg)", fontSize: 12.5, display: "flex", gap: 6, alignItems: "flex-start", marginTop: 8 }}>
      <Icon name="alert" size={14} style={{ flex: "none", marginTop: 1 }} />
      <span>{!error.uncertain && <span>{mcT("Not saved")}{": "}</span>}<span>{mcT(error.message)}</span></span>
    </div>
  );
}

const MC_STATUS = {
  active: ["Active", "green"], completed: ["Completed", "blue"], expired: ["Expired", "gray"],
  awaiting_payment: ["Awaiting payment", "amber"], review: ["Needs the owner's review", "amber"], void: ["Void", "gray"],
};
const MC_FOLLOW = {
  overdue: ["Overdue", "red"], due: ["Due", "amber"], upcoming: ["Upcoming", "blue"], scheduled: ["Scheduled", "green"],
  missing_contact: ["Missing contact information", "gray"],
};
function ContractStatusBadge({ status }) { const m = MC_STATUS[status] || [status, "gray"]; return <Badge color={m[1]} dot>{mcT(m[0])}</Badge>; }
function FollowupStatusBadge({ status }) { const m = MC_FOLLOW[status] || [status, "gray"]; return <Badge color={m[1]} dot>{mcT(m[0])}</Badge>; }

// ── parts: included in the contract, free to the customer ──────────────────────────────────────────
// Everyone sees the part names and quantities. The founders also see each part's inventory cost
// (FIFO average now; the contract records the real cost once the visit is recorded), the total, the
// contract value and what is left after parts, before labour.
function ContractParts({ parts, onChange, contractValue, testId }) {
  const s = Hydor.getState();
  const d = Hydor.derive();
  const me = Hydor.currentUser();
  const role = me ? me.role : s.meta.role;
  const money = Hydor.can(role, "seeMoney");
  const spares = (s.products || []).filter((p) => p && !p.deleted && (p.category || "") === "Consumables & Spares");
  const choices = spares.length ? spares : (s.products || []).filter((p) => p && !p.deleted);
  const add = (pid) => { if (!pid || parts.some((p) => p.productId === pid)) return; onChange(parts.concat([{ productId: pid, qty: 1 }])); };
  const setQty = (i, q) => onChange(parts.map((p, k) => (k === i ? { ...p, qty: Math.max(1, Math.min(100, parseInt(q, 10) || 1)) } : p)));
  const drop = (i) => onChange(parts.filter((_, k) => k !== i));
  const unitCost = (pid) => { const st = d.productStats[pid]; return st ? Hydor.num(st.avgCost, 0) : 0; };
  const left = (pid) => { const st = d.productStats[pid]; return st ? Hydor.num(st.remaining, 0) : 0; };
  const total = Math.round(parts.reduce((a, p) => a + unitCost(p.productId) * Hydor.num(p.qty, 1), 0) * 1000) / 1000;
  const value = Hydor.num(contractValue, 0);
  const exTax = value > 0 ? Math.round((value / (1 + (d.taxRate || 0))) * 1000) / 1000 : 0;
  return (
    <div data-testid={testId || "contract-parts"} style={{ border: "1px solid var(--border)", borderRadius: 12, overflow: "hidden" }}>
      <div style={{ background: "var(--bg-2)", padding: "10px 14px", borderBottom: "1px solid var(--border)", fontSize: 13, fontWeight: 600, display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
        <span>{mcT("Parts")}</span>
        <Badge color="blue">{mcT("Included in maintenance contract")}</Badge>
      </div>
      <div style={{ padding: "12px 14px" }}>
        <select className="sel" style={{ width: "100%" }} value="" onChange={(e) => add(e.target.value)} data-testid="contract-part-picker">
          <option value="">{mcT("+ Add a part")}</option>
          {choices.map((p) => <option key={p.id} value={p.id}>{p.code} · {p.name}</option>)}
        </select>
      </div>
      {parts.length > 0 && (
        <div style={{ borderTop: "1px solid var(--border)", padding: "10px 14px", display: "flex", flexDirection: "column", gap: 8 }}>
          {parts.map((p, i) => {
            const prod = (s.products || []).find((x) => x.id === p.productId) || {};
            const short = Hydor.can(role, "seeAllSales") && left(p.productId) + 0.0005 < Hydor.num(p.qty, 1);
            return (
              <div key={p.productId} data-testid={"contract-part-" + p.productId} style={{ background: "var(--bg-2)", borderRadius: 8, padding: "8px 10px" }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                  <span style={{ flex: 1, fontSize: 13 }}>{prod.code ? prod.code + " · " : ""}{prod.name || p.productId}</span>
                  <input className="inp num" type="number" min="1" max="100" step="1" value={p.qty} onChange={(e) => setQty(i, e.target.value)} style={{ width: 64 }} aria-label={mcT("Quantity")} />
                  <span style={{ fontSize: 12, color: "var(--pos)", fontWeight: 600 }}>{mcT("Included")}</span>
                  {money && <span data-testid="part-cost" style={{ fontSize: 12, color: "var(--text-2)", whiteSpace: "nowrap" }}>{Hydor.fmtMoney(unitCost(p.productId))} × {p.qty} = <b>{Hydor.fmtMoney(unitCost(p.productId) * Hydor.num(p.qty, 1))}</b></span>}
                  <button onClick={() => drop(i)} aria-label={mcT("Remove")} style={{ background: "none", border: "none", cursor: "pointer", color: "var(--text-3)", fontSize: 14 }}>✕</button>
                </div>
                {short && <div style={{ fontSize: 11.5, color: "var(--neg)", marginTop: 4 }}>{mcT("Not enough stock")} ({Hydor.fmtQty(left(p.productId))})</div>}
              </div>
            );
          })}
        </div>
      )}
      {money && (parts.length > 0 || value > 0) && (
        <div data-testid="contract-owner-figures" style={{ borderTop: "1px solid var(--border)", padding: "10px 14px", fontSize: 12.5, display: "grid", gridTemplateColumns: "1fr auto", gap: "4px 12px" }}>
          <span style={{ color: "var(--text-3)" }}>{mcT("Parts cost (inventory, now)")}</span><b className="hx-num">{Hydor.fmtMoney(total)} JOD</b>
          {value > 0 && <><span style={{ color: "var(--text-3)" }}>{mcT("Contract value (tax included)")}</span><b className="hx-num">{Hydor.fmtMoney(value)} JOD</b></>}
          {value > 0 && <><span style={{ color: "var(--text-3)" }}>{mcT("Value after parts, before labour (excl. tax)")}</span><b className="hx-num" style={{ color: exTax - total >= 0 ? "var(--pos)" : "var(--neg)" }}>{Hydor.fmtMoney(exTax - total)} JOD</b></>}
        </div>
      )}
    </div>
  );
}

// ── activation ─────────────────────────────────────────────────────────────────────────────────
function ContractActivationForm({ header, customers, onClose, presetPartyId }) {
  const push = useToast();
  const act = useContractAction();
  const s = Hydor.getState();
  const me = Hydor.currentUser();
  const role = me ? me.role : s.meta.role;
  const MC = mcMC();
  const today = Hydor.businessToday();
  const canPickStart = Hydor.can(role, "editTransactions");
  const [f, setF] = React.useState({ partyId: presetPartyId || "", value: "", email: "", start: today, paid: false, parts: [], signature: "", notes: "", signing: false, done: null });
  const set = (k, v) => setF((p) => ({ ...p, [k]: v, ...(["value", "start", "partyId"].includes(k) ? { signature: "" } : {}) }));
  const party = (s.parties || []).find((p) => p && p.id === f.partyId) || null;
  const emailOnFile = party && String(party.email || "").trim();
  const value = Hydor.num(f.value, 0);
  const plan = MC ? MC.planForValue(value) : { visits: 3 };
  const expiry = MC ? MC.expiryFor(f.start || today, 1) : "";
  const errs = MC ? MC.activationErrors({ partyId: f.partyId, contractValue: value, paymentConfirmed: f.paid, contractSignature: f.signature, parts: f.parts }) : [{ code: "unavailable", message: "Reload the app." }];
  const emailBad = !emailOnFile && String(f.email || "").trim() !== "" && !(MC && MC.isPlainAddress(String(f.email).trim()));
  const existing = f.partyId ? Hydor.activeMaintenanceContractFor(f.partyId) : null;
  const ready = !!MC && errs.length === 0 && !emailBad && !act.busy;
  const activate = async () => {
    const r = await act.run("activate", { partyId: f.partyId, contractValue: value, paymentConfirmed: f.paid, contractSignature: f.signature,
      parts: f.parts, notes: f.notes, startDate: f.start !== today ? f.start : "", customerEmail: emailOnFile ? "" : String(f.email || "").trim() });
    if (r.ok) {
      push(mcT(plan.visits === 2 ? "Contract activated — visit 1 of 2 recorded." : "Contract activated — visit 1 of 3 recorded."));
      set("done", (r.result && r.result.contractId) || "");
    }
  };
  if (f.done) {
    const row = Hydor.maintenanceContractRow(f.done);
    return (
      <Modal title={mcT("Maintenance contract activated")} onClose={onClose} foot={<Btn variant="primary" onClick={onClose}>{mcT("Done")}</Btn>}>
        {row ? <ContractSummary row={row} /> : <p className="hx-sub">{mcT("The contract is recorded.")}</p>}
      </Modal>
    );
  }
  return (
    <Modal title={mcT("New maintenance contract")} onClose={onClose} foot={<>
      <Btn variant="ghost" onClick={onClose}>{mcT("Cancel")}</Btn>
      <Btn variant="primary" icon="check" data-testid="contract-activate" disabled={!ready} onClick={activate}>{act.busy ? mcT("Activating…") : mcT("Activate contract and record first visit")}</Btn>
    </>}>
      {header}
      <div className="form-grid">
        <Field label={mcT("Customer") + " *"} span2>
          <SearchSelect value={f.partyId} onChange={(e) => setF((p) => ({ ...p, partyId: e.target.value, email: "", signature: "" }))} placeholder={mcT("— select customer —")}
            options={(customers || []).map((c) => ({ value: c.id, label: c.name + (c.phone ? " · " + c.phone : "") }))} />
        </Field>
        {existing && <div className="span2" data-testid="contract-existing" style={{ fontSize: 12.5, color: "var(--warn)" }}>{mcT("This customer already has an active contract")} — {mcT("expires on")} {existing.expiryDate}</div>}
        {f.partyId && (emailOnFile
          ? <Field label={mcT("Customer email")} span2 hint={mcT("From the customer record")}><input className="inp" value={emailOnFile} readOnly disabled data-testid="contract-email-onfile" /></Field>
          : <Field label={mcT("Customer email")} span2 hint={mcT("Saved to the customer record")} error={emailBad ? mcT("The customer email is not a valid address.") : undefined}>
              <input className="inp" type="email" value={f.email} onChange={(e) => set("email", e.target.value)} placeholder="customer@email.com" data-testid="contract-email" />
            </Field>)}
        <Field label={mcT("Contract value (tax included)") + " *"} span2 hint={mcT("Minimum 75.000 JOD, tax included")}
          error={f.value !== "" && value < 75 ? mcT("A maintenance contract is at least 75.000 JOD, tax included.") : undefined}>
          <Affix affix="JOD"><input className="inp num" type="number" step="0.001" min="75" value={f.value} onChange={(e) => set("value", e.target.value)} placeholder="75.000" style={{ paddingRight: 44 }} data-testid="contract-value" /></Affix>
        </Field>
        {value >= 75 && <div className="span2" data-testid="contract-plan" style={{ color: "var(--accent)", fontWeight: 600 }}>
          {mcT(plan.visits === 2 ? "2 visits in one year: today and after 6 months." : "3 visits in one year: today, after 4 months and after 8 months.")}
        </div>}
        <Field label={mcT("Payment and contract start date")} span2 hint={expiry ? mcT("Valid for one year — expires on") + " " + expiry : undefined}>
          <input className="inp" type="date" value={f.start} max={today} disabled={!canPickStart} onChange={(e) => set("start", e.target.value || today)} data-testid="contract-start" />
        </Field>
        <label className="span2" style={{ display: "flex", alignItems: "center", gap: 9, fontSize: 13.5, fontWeight: 500, cursor: "pointer" }}>
          <input type="checkbox" checked={f.paid} onChange={(e) => set("paid", e.target.checked)} data-testid="contract-paid" />
          <span>{mcT("Full payment received")}</span>{value > 0 && <b className="hx-num">{Hydor.fmtMoney(value)} JOD</b>}
        </label>
        <div className="span2" style={{ fontSize: 13, color: "var(--text-2)" }} data-testid="contract-seller"><span>{mcT("Sold by")}:</span> <b>{me ? me.name : "—"}</b></div>
        <div className="span2"><ContractParts parts={f.parts} onChange={(p) => set("parts", p)} contractValue={value} testId="contract-parts-activation" /></div>
        <div className="span2">
          {f.signature ? (
            <div data-testid="contract-signed" style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", border: "1px solid var(--pos)", borderRadius: 12, flexWrap: "wrap" }}>
              <span style={{ fontSize: 13.5, fontWeight: 600, color: "var(--pos)" }}>✓ {mcT("Signed by the customer")}</span>
              <img src={f.signature} alt={mcT("Customer signature")} style={{ height: 34, background: "#fff", borderRadius: 6, padding: "2px 8px" }} />
              <Btn sm variant="ghost" onClick={() => set("signature", "")}>{mcT("Re-sign")}</Btn>
            </div>
          ) : (
            <Btn variant="primary" icon="edit" disabled={!f.partyId || value < 75 || errs.some((e) => e.code === "contract_precision")} data-testid="contract-review-sign" onClick={() => set("signing", true)}>{mcT("Review and sign contract")}</Btn>
          )}
        </div>
        <Field label={mcT("Notes")} span2><input className="inp" value={f.notes} onChange={(e) => set("notes", e.target.value)} placeholder={mcT("Optional")} /></Field>
        {errs.length > 0 && (
          <ul className="span2" data-testid="contract-missing" style={{ margin: 0, paddingInlineStart: 18, fontSize: 12, color: "var(--text-3)" }}>
            {errs.map((e) => <li key={e.code}>{mcT(e.message)}</li>)}
          </ul>
        )}
      </div>
      <ContractError error={act.error} />
      {f.signing && (
        <DrawToSignContract value={value} startDate={f.start} expiryDate={expiry} customerName={party ? party.name : ""} company={s.meta.company}
          onSign={(sig) => setF((p) => ({ ...p, signature: sig, signing: false }))} onCancel={() => set("signing", false)} />
      )}
    </Modal>
  );
}

// ── a contract visit ───────────────────────────────────────────────────────────────────────────
function ContractVisitForm({ contractId, appointmentId, header, onClose }) {
  const push = useToast();
  const act = useContractAction();
  const [parts, setParts] = React.useState([]);
  const [notes, setNotes] = React.useState("");
  const row = Hydor.maintenanceContractRow(contractId);
  const s = Hydor.getState();
  const party = row ? (s.parties || []).find((p) => p && p.id === row.partyId) : null;
  const usable = !!row && row.status === "active" && row.nextVisitNo > 0;
  const complete = async () => {
    const r = await act.run("completeVisit", { contractId, parts, notes, appointmentId: appointmentId || "" });
    if (r.ok) { push(mcT("Maintenance contract visit recorded — 0 JOD charged to the customer.")); onClose(); }
  };
  return (
    <Modal title={mcT("Maintenance contract visit")} onClose={onClose} foot={<>
      <Btn variant="ghost" onClick={onClose}>{mcT("Cancel")}</Btn>
      <Btn variant="primary" icon="check" data-testid="contract-visit-complete" disabled={!usable || act.busy} onClick={complete}>{act.busy ? mcT("Recording…") : mcT("Complete maintenance visit")}</Btn>
    </>}>
      <div data-testid="contract-visit-context" data-contract-id={contractId || ""} data-appointment-id={appointmentId || ""}>
        {header}
      </div>
      {!row ? <Empty icon="search" title={mcT("That maintenance contract was not found.")} /> : <>
        <div style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 14, fontWeight: 600 }}>{party ? party.name : ""}</div>
          <div data-testid="contract-visit-usage" style={{ fontSize: 13, color: "var(--text-2)", marginTop: 4 }}>
            <span>{mcT("Visits used")}</span> <b className="hx-num">{row.visitsUsed}/{row.visitsIncluded}</b> · <span>{mcT("Remaining")}</span> <b className="hx-num">{row.visitsRemaining}</b> · <span>{mcT("Expires on")}</span> <b className="hx-num">{row.expiryDate}</b>
          </div>
          {usable && <div style={{ fontSize: 13, marginTop: 6 }}><span>{mcT("This is visit")}</span> <b className="hx-num">{row.nextVisitNo}</b> <span>{mcT("of")}</span> <b className="hx-num">{row.visitsIncluded}</b></div>}
          {!usable && <ContractError error={{ message: row.status === "expired" ? "This contract has expired — unused visits do not carry forward. The owner can correct it if needed."
            : row.status === "completed" ? "All visits of this contract are already used." : "This contract needs the owner's review before a visit can be recorded." }} />}
        </div>
        <div data-testid="contract-visit-label" style={{ fontSize: 12.5, fontWeight: 600, color: "var(--pos)", marginBottom: 10 }}>{mcT("Maintenance contract visit — included — 0 JOD charged to customer")}</div>
        {usable && <>
          <ContractParts parts={parts} onChange={setParts} testId="contract-parts-visit" />
          <Field label={mcT("Notes")}><input className="inp" value={notes} onChange={(e) => setNotes(e.target.value)} placeholder={mcT("Work done, issues found…")} /></Field>
        </>}
      </>}
      <ContractError error={act.error} />
    </Modal>
  );
}

// ── the contract, at a glance ────────────────────────────────────────────────────────────────
function ContractSummary({ row, onUseVisit, onSchedule, onViewSigned, onCorrect }) {
  if (!row) return null;
  const s = Hydor.getState();
  const me = Hydor.currentUser();
  const role = me ? me.role : s.meta.role;
  const party = (s.parties || []).find((p) => p && p.id === row.partyId) || {};
  const tx = (s.transactions || []).find((t) => t && t.id === row.id) || null;   // this device holds it: its own sale, or management
  const f = row.followup;
  const line = (label, val, id) => (<div data-testid={id} style={{ display: "flex", justifyContent: "space-between", gap: 12, padding: "5px 0", fontSize: 13, borderBottom: "1px solid var(--border-2)" }}><span style={{ color: "var(--text-3)" }}>{mcT(label)}</span><span style={{ fontWeight: 500 }}>{val}</span></div>);
  return (
    <div data-testid={"contract-summary-" + row.id} style={{ border: "1px solid var(--border)", borderRadius: 12, padding: "12px 14px", marginBottom: 12 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }}>
        <b style={{ fontSize: 14 }}>{party.name || "—"}</b><ContractStatusBadge status={row.status} />
      </div>
      {line("Value", tx ? Hydor.fmtMoney(tx.contractValue) + " JOD · " + mcT(tx.paid === false ? "Awaiting payment" : "Paid in full") : mcT(row.status === "awaiting_payment" ? "Awaiting payment" : "Paid in full"), "cs-value")}
      {line("Start date", row.startDate || "—", "cs-start")}
      {line("Expiry date", row.expiryDate || "—", "cs-expiry")}
      {line("Visits completed", row.visitsUsed + " / " + row.visitsIncluded, "cs-used")}
      {line("Visits remaining", String(row.visitsRemaining), "cs-remaining")}
      {line("Last visit", row.lastVisitDate || "—", "cs-last")}
      {row.status === "active" && line(f && f.appointmentId ? "Next visit booked" : "Next contact target", f && f.appointmentId ? String(f.scheduledFor || "").replace("T", " ") : (f ? f.dueDate : row.nextTargetDate) || "—", "cs-next")}
      <div style={{ display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap" }}>
        {row.signed && tx && tx.contractSignature && onViewSigned && <Btn sm variant="ghost" icon="file-text" onClick={() => onViewSigned(row)}>{mcT("View signed contract")}</Btn>}
        {row.status === "active" && onUseVisit && <Btn sm variant="primary" icon="tool" onClick={() => onUseVisit(row)}>{mcT("Use a contract visit")}</Btn>}
        {row.status === "active" && onSchedule && Hydor.canU(me, "workMaintenanceFollowups") && <Btn sm variant="ghost" icon="calendar" onClick={() => onSchedule(row)}>{mcT("Schedule next visit")}</Btn>}
        {onCorrect && Hydor.can(role, "correctMaintenanceContracts") && <Btn sm variant="ghost" icon="edit" onClick={() => onCorrect(row)}>{mcT("Correct (owner)")}</Btn>}
      </div>
      {Hydor.can(role, "seeMoney") && <ContractCostPanel contractId={row.id} />}
    </div>
  );
}

// The founders' figures for one contract: every part used under it, at its real (FIFO) cost.
function ContractCostPanel({ contractId }) {
  const c = Hydor.maintenanceContractCosts(contractId);
  if (!c) return null;
  return (
    <div data-testid="contract-costs" style={{ marginTop: 10, fontSize: 12.5 }}>
      {c.lines.length > 0 && (
        <table className="hx-table" style={{ marginBottom: 6 }}>
          <thead><tr><th>{mcT("Visit")}</th><th>{mcT("Part")}</th><th className="r">{mcT("Qty")}</th><th className="r">{mcT("Unit cost")}</th><th className="r">{mcT("Cost")}</th></tr></thead>
          <tbody>{c.lines.map((l, i) => (<tr key={i}><td className="num">{l.visitNo || "—"}</td><td>{l.code ? l.code + " · " : ""}{l.name}</td><td className="r num">{Hydor.fmtQty(l.qty)}</td><td className="r num">{Hydor.fmtMoney(l.unitCost)}</td><td className="r num">{Hydor.fmtMoney(l.cost)}</td></tr>))}</tbody>
        </table>
      )}
      <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: "3px 12px" }}>
        <span style={{ color: "var(--text-3)" }}>{mcT("Total parts cost")}</span><b className="hx-num">{Hydor.fmtMoney(c.totalPartsCost)} JOD</b>
        <span style={{ color: "var(--text-3)" }}>{mcT("Contract value (tax included)")}</span><b className="hx-num">{Hydor.fmtMoney(c.contractValue)} JOD</b>
        <span style={{ color: "var(--text-3)" }}>{mcT("Value after parts, before labour (excl. tax)")}</span><b className="hx-num" style={{ color: c.afterParts >= 0 ? "var(--pos)" : "var(--neg)" }}>{Hydor.fmtMoney(c.afterParts)} JOD</b>
      </div>
      {c.overValue && <div data-testid="contract-over-value" style={{ marginTop: 6, color: "var(--warn)" }}><Icon name="alert" size={13} /> {mcT("The parts used have cost more than the contract value.")}</div>}
    </div>
  );
}

// The signed version, as it was signed (this device holds the contract record).
function SignedContractView({ contractId, onClose }) {
  const s = Hydor.getState();
  const tx = (s.transactions || []).find((t) => t && t.id === contractId) || null;
  const party = tx ? (s.parties || []).find((p) => p && p.id === tx.partyId) || {} : {};
  return (
    <Modal title={mcT("Signed maintenance contract")} onClose={onClose} foot={<Btn variant="primary" onClick={onClose}>{mcT("Close")}</Btn>}>
      {!tx || !tx.contractSignature ? <p className="hx-sub">{mcT("The signed contract is kept with the contract record.")}</p> : (
        <div data-testid="signed-contract" style={{ fontSize: 13, lineHeight: 1.7 }}>
          <div><b>{party.name || "—"}</b></div>
          <div><span>{mcT("Contract value (tax included)")}</span>: <b className="hx-num">{Hydor.fmtMoney(tx.contractValue)} JOD</b> · <span>{mcT("Paid in full")}</span></div>
          <div><span>{mcT("Start date")}</span>: <b className="hx-num">{tx.contractStartDate || tx.date}</b> · <span>{mcT("Expiry date")}</span>: <b className="hx-num">{tx.contractExpiryDate || ""}</b></div>
          <div><span>{mcT(mcMC().planForContract(tx).visits === 2 ? "Two maintenance visits: visit 1 at activation, follow-up after 6 months. Unused visits expire." : "Three maintenance visits: visit 1 at activation, visits 2 and 3 any time before expiry. Unused visits expire.")}</span></div>
          <div><span>{mcT("Parts installed in contract visits are included at no charge.")}</span></div>
          <img src={tx.contractSignature} alt={mcT("Customer signature")} style={{ marginTop: 10, maxWidth: "100%", height: 90, background: "#fff", border: "1px solid var(--border)", borderRadius: 8 }} />
          <div style={{ fontSize: 11.5, color: "var(--text-3)", marginTop: 6 }}><span>{mcT("Signed")}</span> {String(tx.contractSignedAt || "").slice(0, 16).replace("T", " ")}{tx.contractTermsVersion ? " · " + tx.contractTermsVersion : ""}</div>
          {tx.contractSignatureRef && <div style={{ fontSize: 11, color: "var(--text-3)", wordBreak: "break-all" }}><span>{mcT("Reference")}</span> {tx.contractSignatureRef}</div>}
        </div>
      )}
    </Modal>
  );
}

// ── the Call Centre's queue ────────────────────────────────────────────────────────────────────
function ContractFollowupSection() {
  const s = useHydor();
  const me = Hydor.currentUser();
  const [contactFor, setContactFor] = React.useState(null);
  const [scheduleFor, setScheduleFor] = React.useState(null);
  if (!Hydor.canU(me, "workMaintenanceFollowups")) return null;
  const rows = Hydor.maintenanceFollowups();
  const party = (id) => (s.parties || []).find((p) => p && p.id === id) || {};
  const uname = (id) => { const u = (s.users || []).find((x) => x.id === id); return u ? u.name : ""; };
  return (
    <div className="hx-panel" data-testid="contract-followups" style={{ overflow: "hidden", marginBottom: 16 }}>
      <div style={{ padding: "16px 20px", borderBottom: "1px solid var(--border)" }}>
        <h3 className="hx-h2">{mcT("Maintenance contracts requiring follow-up")} <span className="hx-num" style={{ color: "var(--text-3)" }}>({rows.length})</span></h3>
      </div>
      {rows.length === 0 ? <Empty icon="check" title={mcT("No maintenance contract needs a call right now.")} /> : (
        <table className="hx-table">
          <thead><tr><th>{mcT("Customer")}</th><th>{mcT("Phone")}</th><th>{mcT("Last visit")}</th><th className="r">{mcT("Visits remaining")}</th><th>{mcT("Expiry")}</th><th>{mcT("Next contact target")}</th><th>{mcT("Status")}</th><th></th></tr></thead>
          <tbody>
            {rows.map((r) => {
              const p = party(r.partyId); const f = r.followup;
              return (
                <tr key={r.id} data-testid={"followup-" + r.id}>
                  <td style={{ fontWeight: 500 }}>{p.name || "—"}</td>
                  <td>{p.phone ? <CallLink phone={p.phone} sm /> : <span style={{ color: "var(--text-3)" }}>—</span>}</td>
                  <td className="num">{r.lastVisitDate || "—"}</td>
                  <td className="r num">{r.visitsRemaining}</td>
                  <td className="num">{r.expiryDate}</td>
                  <td className="num">{f.appointmentId ? String(f.scheduledFor || "").replace("T", " ") + (f.assignedTo ? " · " + uname(f.assignedTo) : "") : f.dueDate}</td>
                  <td><FollowupStatusBadge status={f.status} /></td>
                  <td className="r" style={{ whiteSpace: "nowrap" }}>
                    <Btn sm variant="ghost" icon="phone" onClick={() => setContactFor(r)}>{mcT("Record contact")}</Btn>
                    {!f.appointmentId && <Btn sm variant="primary" icon="calendar" onClick={() => setScheduleFor(r)}>{mcT("Schedule visit")}</Btn>}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      )}
      {contactFor && <ContractContactModal row={contactFor} onClose={() => setContactFor(null)} />}
      {scheduleFor && <ContractScheduleModal row={scheduleFor} onClose={() => setScheduleFor(null)} />}
    </div>
  );
}

function ContractContactModal({ row, onClose }) {
  const push = useToast();
  const act = useContractAction();
  const D = window.HydorDates;
  const today = Hydor.businessToday();
  const dflt = D ? D.addDays(today, 3) : today;
  const [f, setF] = React.useState({ outcome: "reached", next: row.expiryDate && dflt > row.expiryDate ? row.expiryDate : dflt, note: "" });
  const set = (k, v) => setF((p) => ({ ...p, [k]: v }));
  const save = async () => {
    const r = await act.run("recordContact", { contractId: row.id, outcome: f.outcome, nextContactDate: f.next, note: f.note });
    if (r.ok) { push(mcT("Contact recorded.")); onClose(); }
  };
  return (
    <Modal title={mcT("Record contact")} onClose={onClose} foot={<>
      <Btn variant="ghost" onClick={onClose}>{mcT("Cancel")}</Btn>
      <Btn variant="primary" icon="check" disabled={act.busy || !f.next} data-testid="contact-save" onClick={save}>{mcT("Save")}</Btn>
    </>}>
      <div className="form-grid">
        <Field label={mcT("What happened")} span2>
          <select className="sel" value={f.outcome} onChange={(e) => set("outcome", e.target.value)} data-testid="contact-outcome">
            <option value="reached">{mcT("Reached — not scheduled yet")}</option>
            <option value="no_answer">{mcT("No answer")}</option>
            <option value="call_back">{mcT("Asked to call back")}</option>
          </select>
        </Field>
        <Field label={mcT("Next contact date")} span2><input className="inp" type="date" min={today} max={row.expiryDate} value={f.next} onChange={(e) => set("next", e.target.value)} data-testid="contact-next" /></Field>
        <Field label={mcT("Notes")} span2><input className="inp" value={f.note} onChange={(e) => set("note", e.target.value)} placeholder={mcT("Optional")} /></Field>
      </div>
      <ContractError error={act.error} />
    </Modal>
  );
}

function ContractScheduleModal({ row, onClose }) {
  const push = useToast();
  const w = useWrite(push);
  const s = Hydor.getState();
  const me = Hydor.currentUser();
  const f0 = row.followup || {};
  const today = Hydor.businessToday();
  const [f, setF] = React.useState({ date: f0.dueDate && f0.dueDate > today ? f0.dueDate : today, hour: "", minute: "", assignedTo: "", note: "" });
  const [asked, setAsked] = React.useState(false);
  const set = (k, v) => setF((p) => ({ ...p, [k]: v }));
  const issue = asked ? Hydor.timeOfDayIssue(f.hour, f.minute) : "";
  const reps = (s.users || []).filter((u) => u && u.active && !u.deleted);
  const save = () => {
    setAsked(true);
    return w.run(() => Hydor.scheduleContractVisit(row.id, { desiredAt: Hydor.buildAppointmentDateTimeParts(f.date, f.hour, f.minute), assignedTo: f.assignedTo, note: f.note }),
      (o) => { push(o && o.state === "pending" ? mcT("Visit saved on this device — it is booked once it reaches the server.") : mcT("Visit scheduled.")); onClose(); });
  };
  return (
    <Modal title={mcT("Schedule visit")} onClose={onClose} foot={<>
      <WriteError error={w.error} style={{ marginTop: 0, marginInlineEnd: "auto", alignSelf: "center" }} />
      <Btn variant="ghost" onClick={onClose}>{mcT("Cancel")}</Btn>
      <Btn variant="primary" icon="check" disabled={w.busy} data-testid="schedule-save" onClick={save}>{w.busy ? mcT("Saving…") : mcT("Schedule visit")}</Btn>
    </>}>
      <div className="form-grid">
        <div className="span2" style={{ fontSize: 13, color: "var(--text-2)" }}><span>{mcT("Maintenance contract visit")}</span> <b className="hx-num">{row.nextVisitNo}/{row.visitsIncluded}</b> · <span>{mcT("Expires on")}</span> <b className="hx-num">{row.expiryDate}</b></div>
        <Field label={mcT("Appointment date") + " *"}><input className="inp" type="date" min={today} max={row.expiryDate} value={f.date} onChange={(e) => set("date", e.target.value)} /></Field>
        <Field label={mcT("Appointment time") + " *"}>
          <TimeOfDayInput testId="contract-schedule-time" hour={f.hour} minute={f.minute} issue={issue} disabled={w.busy} onChange={(h, m) => setF((p) => ({ ...p, hour: h, minute: m }))} />
        </Field>
        <Field label={mcT("Technician")} span2 hint={mcT("Leave empty to post it to the pool")}>
          <select className="sel" value={f.assignedTo} onChange={(e) => set("assignedTo", e.target.value)}>
            <option value="">{mcT("Pool — anyone can claim")}</option>
            {reps.map((u) => <option key={u.id} value={u.id}>{u.name}</option>)}
          </select>
        </Field>
        <Field label={mcT("Notes")} span2><input className="inp" value={f.note} onChange={(e) => set("note", e.target.value)} placeholder={mcT("Optional")} /></Field>
      </div>
    </Modal>
  );
}

// ── the owner's review and corrections ─────────────────────────────────────────────────────────
function ContractReviewSection() {
  const s = useHydor();
  const me = Hydor.currentUser();
  const [correctFor, setCorrectFor] = React.useState(null);
  if (!me || !Hydor.can(me.role, "correctMaintenanceContracts")) return null;
  const rows = Hydor.maintenanceReviewQueue();
  if (!rows.length) return null;
  const MC = mcMC();
  const party = (id) => (s.parties || []).find((p) => p && p.id === id) || {};
  return (
    <div className="hx-panel hx-panel-pad" data-testid="contract-review" style={{ marginBottom: 16, borderLeft: "3px solid var(--warn)" }}>
      <h3 className="hx-h2" style={{ marginBottom: 10 }}>{mcT("Maintenance contracts to review (owner)")} <span className="hx-num" style={{ color: "var(--text-3)" }}>({rows.length})</span></h3>
      {rows.map((r) => (
        <div key={r.id} data-testid={"review-" + r.id} style={{ display: "flex", gap: 10, alignItems: "flex-start", padding: "10px 0", borderBottom: "1px solid var(--border-2)" }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 13, fontWeight: 600 }}>{party(r.partyId).name || "—"} <span style={{ color: "var(--text-3)", fontWeight: 400 }}>· {r.startDate || "—"} · {r.visitsUsed}/{r.visitsIncluded}</span></div>
            <ul style={{ margin: "4px 0 0", paddingInlineStart: 18, fontSize: 12, color: "var(--text-2)" }}>
              {(r.review || []).map((c) => <li key={c}>{mcT((MC && MC.REVIEW[c]) || c)}</li>)}
            </ul>
          </div>
          <Btn sm variant="primary" icon="edit" onClick={() => setCorrectFor(r)}>{mcT("Correct")}</Btn>
        </div>
      ))}
      {correctFor && <ContractCorrectionModal row={correctFor} onClose={() => setCorrectFor(null)} />}
    </div>
  );
}

const MC_CORRECTIONS = [
  ["historicalVisit", "Record a past visit"], ["resolveReview", "Mark as reviewed"], ["setStart", "Set the start date"],
  ["reverseVisit", "Reverse a recorded visit (its parts return to stock)"], ["extend", "Extend the expiry"],
  ["addVisit", "Add a visit"], ["voidContract", "Void the contract (with its recorded visits)"],
];
function ContractCorrectionModal({ row, onClose }) {
  const push = useToast();
  const act = useContractAction();
  const today = Hydor.businessToday();
  const visits = (row.visits || []).filter((v) => v.source === "visit" && v.txId);
  const avail = MC_CORRECTIONS.filter(([k]) => (k !== "setStart" || !row.managed) && (k !== "reverseVisit" || visits.length > 0) && (k !== "resolveReview" || (row.review || []).length > 0));
  const review = row.review || [];
  // Start on the correction that actually closes the displayed legacy-data warning. Previously
  // every card defaulted to "Record a past visit", including start-date mismatches and unrelated
  // visits; the server recorded that action correctly but the original warning necessarily stayed.
  const recommended = review.some((c) => c === "start_date_missing" || c === "start_date_differs") ? "setStart"
    : review.length === 1 && review[0] === "first_visit_unproven" ? "historicalVisit"
    : review.length ? "resolveReview" : (avail[0] ? avail[0][0] : "");
  const initialCorrection = avail.some(([k]) => k === recommended) ? recommended : (avail[0] ? avail[0][0] : "");
  const [f, setF] = React.useState({ correction: initialCorrection, date: row.startDate || today, visitTxId: visits.length ? visits[visits.length - 1].txId : "", reason: "" });
  const set = (k, v) => setF((p) => ({ ...p, [k]: v }));
  const needsDate = f.correction === "extend" || f.correction === "historicalVisit" || f.correction === "setStart";
  const reasonOk = String(f.reason || "").trim().length >= 3;
  const save = async () => {
    const r = await act.run("correct", { contractId: row.id, correction: f.correction, reason: f.reason, date: needsDate ? f.date : "", visitTxId: f.correction === "reverseVisit" ? f.visitTxId : "" });
    if (r.ok) { push(mcT("Correction recorded.")); onClose(); }
  };
  return (
    <Modal title={mcT("Correct maintenance contract")} onClose={onClose} foot={<>
      <Btn variant="ghost" onClick={onClose}>{mcT("Cancel")}</Btn>
      <Btn variant="primary" icon="check" data-testid="correction-save" disabled={act.busy || !reasonOk || !f.correction} onClick={save}>{mcT("Record correction")}</Btn>
    </>}>
      <div className="form-grid">
        <Field label={mcT("Correction")} span2>
          <select className="sel" value={f.correction} onChange={(e) => set("correction", e.target.value)} data-testid="correction-type">
            {avail.map(([k, label]) => <option key={k} value={k}>{mcT(label)}</option>)}
          </select>
        </Field>
        {f.correction === "reverseVisit" && (
          <Field label={mcT("Visit")} span2>
            <select className="sel" value={f.visitTxId} onChange={(e) => set("visitTxId", e.target.value)}>
              {visits.map((v) => <option key={v.txId} value={v.txId}>{mcT("Visit")} {v.no} · {v.date}</option>)}
            </select>
          </Field>
        )}
        {needsDate && <Field label={mcT(f.correction === "extend" ? "New expiry date" : f.correction === "setStart" ? "Start date" : "Visit date")} span2>
          <input className="inp" type="date" value={f.date} onChange={(e) => set("date", e.target.value)} max={f.correction === "extend" ? undefined : today} />
        </Field>}
        <Field label={mcT("Reason (required)")} span2 hint={mcT("Recorded with your name in the audit trail")}>
          <input className="inp" value={f.reason} onChange={(e) => set("reason", e.target.value)} data-testid="correction-reason" />
        </Field>
      </div>
      <ContractError error={act.error} />
    </Modal>
  );
}

// ── where a contract is opened from (a transaction, a customer, a booked visit) ─────────────────
// One component owns the dialogs a summary can open, so every place shows the same contract and
// offers the same actions: View signed contract, Use a contract visit, Schedule next visit, and the
// owner's audited correction.
function ContractDrill({ contractId, appointmentId }) {
  useHydor();
  const [view, setView] = React.useState("");
  const row = Hydor.maintenanceContractRow(contractId);
  if (!row) return null;
  return (
    <div data-testid={"contract-drill-" + contractId}>
      <ContractSummary row={row} onViewSigned={() => setView("signed")} onUseVisit={() => setView("visit")}
        onSchedule={() => setView("schedule")} onCorrect={() => setView("correct")} />
      {view === "signed" && <SignedContractView contractId={row.id} onClose={() => setView("")} />}
      {view === "visit" && <ContractVisitForm contractId={row.id} appointmentId={appointmentId || ""} onClose={() => setView("")} />}
      {view === "schedule" && <ContractScheduleModal row={row} onClose={() => setView("")} />}
      {view === "correct" && <ContractCorrectionModal row={row} onClose={() => setView("")} />}
    </div>
  );
}
// A customer's contracts, newest active first.
function CustomerContracts({ partyId }) {
  useHydor();
  const rows = Hydor.maintenanceContractsFor(partyId);
  if (!rows.length) return null;
  return (
    <div data-testid="customer-contracts" style={{ marginBottom: 16 }}>
      <div className="hx-eyebrow" style={{ marginBottom: 6 }}>{mcT("Maintenance contracts")}</div>
      {rows.map((r) => <ContractDrill key={r.id} contractId={r.id} />)}
    </div>
  );
}
// A contract visit, as a transaction: never a sale — the contract it belongs to, and what it charged.
function ContractVisitDrill({ tx }) {
  if (!tx) return null;
  return (
    <div data-testid="contract-visit-drill" style={{ marginBottom: 16 }}>
      <div style={{ fontSize: 13, fontWeight: 600, color: "var(--pos)", marginBottom: 8 }}>{mcT("Maintenance contract visit — included — 0 JOD charged to customer")}</div>
      <ContractDrill contractId={tx.contractId} />
    </div>
  );
}

Object.assign(window, {
  useContractAction, ContractParts, ContractActivationForm, ContractVisitForm, ContractSummary, ContractCostPanel, SignedContractView,
  ContractFollowupSection, ContractContactModal, ContractScheduleModal, ContractReviewSection, ContractCorrectionModal,
  ContractStatusBadge, FollowupStatusBadge, ContractDrill, CustomerContracts, ContractVisitDrill,
});
