// EzClaim — payment, secure details, submission, status screens.

const { fmtUSD: pFmtUSD, fmtDate: pFmtDate, fmtTime: pFmtTime, shortId, lookup: pLookup, homesteadStatus: pHomesteadStatus } = window.EZ_MOCK;

function getStatusToken(session) {
  return session?.status_token || session?.draft_filing?.status_token || "";
}

function getStatusUrl(session) {
  const token = getStatusToken(session);
  return session?.status_url || session?.draft_filing?.status_url || (token ? `${window.location.origin}/s/${encodeURIComponent(token)}` : "");
}

function mergeStatusRecord(session, record) {
  if (!record) return session;
  return {
    ...session,
    filing_id: record.filing_id || session.filing_id,
    status_token: record.status_token || session.status_token,
    status_url: record.status_url || session.status_url,
    filing: record.filing || session.filing,
    lookup: record.lookup?.matches?.length ? record.lookup : session.lookup,
    payment: record.payment && Object.keys(record.payment).length ? record.payment : session.payment,
    events: record.events?.length ? record.events : session.events,
    status: record.status || session.status,
    sdat_status: record.sdat_status || session.sdat_status,
    submitted_at: record.submitted_at || session.submitted_at,
  };
}

// ─────────────────────────────────────────────────────────────────────────────
// Stripe handoff (loading)
// ─────────────────────────────────────────────────────────────────────────────
function CheckoutScreen({ nav, session, setSession }) {
  const [retrying, setRetrying] = useState(false);
  const [error, setError] = useState("");

  async function retryCheckout() {
    const filingId = session?.filing_id || session?.draft_filing?.filing_id || "";
    if (!filingId) {
      nav("/review");
      return;
    }

    setRetrying(true);
    setError("");
    try {
      const response = await fetch("/api/checkout", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ filing_id: filingId }),
      });
      const checkout = await response.json().catch(() => ({}));
      if (response.ok && checkout.checkout_url) {
        window.location.href = checkout.checkout_url;
        return;
      }
      setError("Secure checkout is still unavailable. Return to review and try again.");
    } catch (_error) {
      setError("Secure checkout is still unavailable. Check your connection and try again.");
    }
    setRetrying(false);
  }

  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 18 }}>
      <Button kind="ghost" size="sm" icon={<I.ArrowLeft size={14}/>} onClick={() => nav("/review")}>Back to review</Button>

      <div className="card lg">
        <div style={{ display: "flex", flexDirection: "column", gap: 14, alignItems: "center", textAlign: "center" }}>
          <div style={{ width: 52, height: 52, borderRadius: 16, background: "var(--brand-50)", color: "var(--brand-700)", display: "grid", placeItems: "center" }}>
            <I.Lock size={22}/>
          </div>
          <div>
            <div className="h-section">Secure checkout could not open</div>
            <p className="subtle" style={{ marginTop: 8 }}>We could not redirect you to Stripe from this tab. Your filing draft is saved, and no payment has been collected.</p>
          </div>
        </div>
        {error ? (
          <Callout kind="err" icon={<I.Alert size={16}/>} title="Checkout paused">
            {error}
          </Callout>
        ) : null}
        <div className="cta-bar" style={{ marginTop: 18 }}>
          <Button kind="primary" block onClick={retryCheckout} disabled={retrying} iconRight={<I.Lock size={14}/>}>
            {retrying ? "Opening Stripe..." : "Retry secure checkout"}
          </Button>
          <Button kind="secondary" block onClick={() => nav("/review")}>Back to review</Button>
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Secure details — collected AFTER payment
// ─────────────────────────────────────────────────────────────────────────────
const TAX_ID_RE = /^\d{3}-\d{2}-\d{4}$/;

function SecureDetailsScreen({ nav, session, setSession }) {
  const f = session?.filing || {};
  const owners = session?.filing?.owners?.length ? session.filing.owners : [{ name: f.full_name || "Primary homeowner", role: "homeowner" }];
  const [secure, setSecure] = useState(() => ({
    taxIdsByOwnerId: buildTaxIdsByOwnerId(owners),
    consent: Boolean(session.secure?.consent),
  }));
  const sigRef = useRef(null);
  const [drew, setDrew] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState("");
  const taxIdsComplete = owners.every((owner, index) => TAX_ID_RE.test(secure.taxIdsByOwnerId?.[ownerKey(owner, index)] || ""));

  function setS(patch) { setSecure(prev => ({ ...prev, ...patch })); }
  function setOwnerTaxId(index, value) {
    const key = ownerKey(owners[index], index);
    setSecure((prev) => ({
      ...prev,
      taxIdsByOwnerId: {
        ...(prev.taxIdsByOwnerId || {}),
        [key]: value,
      },
    }));
  }

  // Tiny signature pad
  useEffect(() => {
    const c = sigRef.current; if (!c) return;
    const ctx = c.getContext("2d");
    ctx.lineWidth = 1.8; ctx.lineCap = "round"; ctx.strokeStyle = "#0B1F33";
    let drawing = false; let last = null;
    function pt(e) {
      const r = c.getBoundingClientRect();
      const x = (e.touches ? e.touches[0].clientX : e.clientX) - r.left;
      const y = (e.touches ? e.touches[0].clientY : e.clientY) - r.top;
      return [x * (c.width / r.width), y * (c.height / r.height)];
    }
    function down(e) { e.preventDefault(); drawing = true; last = pt(e); setDrew(true); }
    function move(e) {
      if (!drawing) return; e.preventDefault();
      const p = pt(e);
      ctx.beginPath(); ctx.moveTo(last[0], last[1]); ctx.lineTo(p[0], p[1]); ctx.stroke();
      last = p;
    }
    function up() { drawing = false; last = null; }
    c.addEventListener("mousedown", down); c.addEventListener("mousemove", move); window.addEventListener("mouseup", up);
    c.addEventListener("touchstart", down); c.addEventListener("touchmove", move); window.addEventListener("touchend", up);
    return () => {
      c.removeEventListener("mousedown", down); c.removeEventListener("mousemove", move); window.removeEventListener("mouseup", up);
      c.removeEventListener("touchstart", down); c.removeEventListener("touchmove", move); window.removeEventListener("touchend", up);
    };
  }, []);
  function clearSig() {
    const c = sigRef.current; if (!c) return;
    c.getContext("2d").clearRect(0,0,c.width,c.height);
    setDrew(false);
  }

  async function submit() {
    if (!taxIdsComplete || !secure.consent || !drew || submitting) return;
    const filingId = session?.filing_id || session?.draft_filing?.filing_id || "";
    const statusToken = getStatusToken(session);
    const checkoutSessionId = session?.payment?.checkout_session_id
      || session?.draft_filing?.payment?.checkout_session_id
      || session?.submission?.checkout_session_id
      || "";
    const signaturePng = sigRef.current?.toDataURL("image/png") || "";
    const securePayload = {
      owners: owners.map((owner, index) => ({
        name: owner.name,
        ssn_or_itin: secure.taxIdsByOwnerId?.[ownerKey(owner, index)] || "",
      })),
      primary_signature_png: signaturePng,
      coowner_signature_png: owners.length > 1 ? signaturePng : "",
      consent: true,
    };
    if (!filingId) {
      setSubmitError("We could not find your saved filing. Return to review and try again.");
      return;
    }

    setSubmitting(true);
    setSubmitError("");
    try {
      const response = await fetch("/api/submissions", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          filing_id: filingId,
          status_token: statusToken,
          checkout_session_id: checkoutSessionId,
          secure: securePayload,
        }),
      });
      const submission = await response.json().catch(() => ({}));
      if (!response.ok) {
        setSubmitError(submission.error === "payment_required"
          ? "Stripe is still confirming payment. Wait a moment and try again from secure details or review."
          : "We could not submit your filing yet. Your draft is saved; try again from review.");
        return;
      }

      const signedAt = new Date().toISOString();
      const sanitizedOwners = owners.map((owner, index) => {
        const taxId = secure.taxIdsByOwnerId?.[ownerKey(owner, index)] || "";
        return {
          owner_id: ownerKey(owner, index),
          name: owner.name,
          tax_id_last4: taxIdLast4(taxId),
          masked_tax_id: maskSsn(taxId),
        };
      });

      setSession({
        ...session,
        filing_id: submission.filing_id || filingId,
        status_token: submission.status_token || getStatusToken(session),
        status_url: submission.status_url || getStatusUrl(session),
        secure: {
          owners: sanitizedOwners,
          consent: true,
          signature_drawn: true,
          signed_at: signedAt,
        },
        submission,
        status: submission.status,
        events: submission.events || session.events,
        sdat_status: submission.sdat_status || session.sdat_status,
      });
      nav("/submitting");
    } catch (_error) {
      setSubmitError("We could not reach the filing server. Your draft is saved; try again from review.");
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 18 }}>
      <div className="row gap-3" style={{ alignItems: "center" }}>
        <Tag kind="good"><I.Check size={11}/> Paid · $39.99</Tag>
        <span className="muted" style={{ fontSize: 12 }}>Payment confirmed</span>
      </div>
      <div>
        <h2 className="h-page">Final filing details</h2>
        <p className="subtle" style={{ marginTop: 6 }}>
          SDAT requires SSN or ITIN and a signature on the form. We collect them only after payment.
        </p>
      </div>

      <Callout kind="" icon={<I.Lock size={16}/>} title="What we do with this data">
        Used only to prepare, review, and submit your filing. Sensitive filing materials are encrypted for the review queue.
      </Callout>

      <div className="card lg">
        <h3 className="h-section">Owner SSN or ITIN</h3>
        {owners.map((owner, index) => (
          <Field key={index} label={`${owner.name || `Owner ${index + 1}`} SSN or ITIN`}>
            <Input
              aria-label={`${owner.name || `Owner ${index + 1}`} SSN or ITIN`}
              value={secure.taxIdsByOwnerId?.[ownerKey(owner, index)] || ""}
              onChange={(e) => setOwnerTaxId(index, formatSsn(e.target.value))}
              placeholder="___-__-____"
              inputMode="numeric"
              maxLength={11}
            />
          </Field>
        ))}
      </div>

      <div className="card lg">
        <h3 className="h-section">Signature</h3>
        <p className="subtle" style={{ marginBottom: 12 }}>Sign with your finger or mouse. This becomes the signature on your SDAT application.</p>
        <div style={{ border: "1px dashed var(--line-2)", borderRadius: "var(--radius)", background: "var(--surface-2)", position: "relative", height: 160, overflow: "hidden" }}>
          <canvas ref={sigRef} width="900" height="240" style={{ width: "100%", height: "100%", display: "block", touchAction: "none" }}/>
          {!drew ? <span style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", color: "var(--ink-400)", fontSize: 13, pointerEvents: "none" }}>Sign here</span> : null}
        </div>
        <div className="row between mt-2">
          <span className="muted" style={{ fontSize: 12 }}>{f.full_name || "Your name"}</span>
          <Button kind="link" size="sm" onClick={clearSig}>Clear</Button>
        </div>
      </div>

      <Checkbox
        checked={!!secure.consent}
        onChange={(v) => setS({ consent: v })}
        label="I authorize EzClaim to prepare and submit my Homestead Tax Credit application to SDAT."
        sub="I confirm the information I've provided is true to the best of my knowledge. I understand approval is determined by SDAT, not EzClaim."
      />

      {submitError ? (
        <Callout kind="err" icon={<I.Alert size={16}/>} title="Submission paused">
          {submitError}
        </Callout>
      ) : null}

      <div className="cta-bar sticky">
        <Button kind="secondary" onClick={() => nav("/review")} icon={<I.ArrowLeft size={14}/>}>Back</Button>
        <Button kind="primary" onClick={submit} iconRight={<I.ArrowRight size={14}/>} disabled={!taxIdsComplete || !secure.consent || !drew || submitting}>
          {submitting ? "Submitting..." : "Submit my filing"}
        </Button>
      </div>
    </div>
  );
}

function buildTaxIdsByOwnerId(owners) {
  return Object.fromEntries(owners.map((owner, index) => [ownerKey(owner, index), ""]));
}

function ownerKey(owner, index) {
  return owner?.owner_id || [owner?.name, owner?.dob, owner?.role].filter(Boolean).join("|") || `owner_${index + 1}`;
}

function formatSsn(s) {
  const d = s.replace(/\D/g, "").slice(0, 9);
  if (d.length <= 3) return d;
  if (d.length <= 5) return d.slice(0,3) + "-" + d.slice(3);
  return d.slice(0,3) + "-" + d.slice(3,5) + "-" + d.slice(5);
}
function maskSsn(s) { return "•••-••-" + s.slice(-4); }
function taxIdLast4(s) { return s.replace(/\D/g, "").slice(-4); }

// ─────────────────────────────────────────────────────────────────────────────
// Submitting — animated progress
// ─────────────────────────────────────────────────────────────────────────────
function SubmittingScreen({ nav, session, setSession }) {
  const STEPS = [
    { k: "build",   t: "Building your filing packet",   d: 1200 },
    { k: "review",  t: "Queueing for internal review",  d: 1400 },
  ];
  const [i, setI] = useState(0);
  const [done, setDone] = useState(false);
  const [failed, setFailed] = useState(false);
  const [submitError, setSubmitError] = useState("");
  const acceptedSubmission = session.submission?.status === "pending_review" ? session.submission : null;

  useEffect(() => {
    if (!acceptedSubmission) {
      setSubmitError("We do not have an accepted submission for this filing. Return to review and try again.");
      setFailed(true);
      return;
    }
    if (done || failed) return;
    if (i >= STEPS.length) { setDone(true); return; }
    const t = setTimeout(() => setI(i + 1), STEPS[i].d);
    return () => clearTimeout(t);
  }, [i, done, failed, acceptedSubmission]);

  useEffect(() => {
    if (!done || !acceptedSubmission) return;
    let cancelled = false;
    setSession({
      ...session,
      filing_id: acceptedSubmission.filing_id || session.filing_id,
      status_token: acceptedSubmission.status_token || getStatusToken(session),
      status_url: acceptedSubmission.status_url || getStatusUrl(session),
      events: acceptedSubmission.events || session.events,
      sdat_status: acceptedSubmission.sdat_status || session.sdat_status,
      status: acceptedSubmission.status || session.status,
    });
    const t = setTimeout(() => {
      if (!cancelled) nav("/submitted");
    }, 700);
    return () => { cancelled = true; clearTimeout(t); };
  }, [done, acceptedSubmission]);

  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 18, paddingTop: 24 }}>
      <div className="text-center">
        <h2 className="h-page">Preparing your review packet</h2>
        <p className="subtle" style={{ marginTop: 6 }}>Stay on this page while we confirm the secure queue handoff.</p>
      </div>
      {submitError ? (
        <Callout kind="err" icon={<I.Alert size={18}/>} title="Submission paused">
          {submitError}
        </Callout>
      ) : null}
      <div className="card lg">
        {STEPS.map((s, idx) => (
          <div key={s.k} className="row gap-3" style={{ padding: "10px 0", alignItems: "center" }}>
            <span style={{
              width: 28, height: 28, borderRadius: "50%",
              background: idx < i ? "var(--good-600)" : idx === i ? "var(--brand-600)" : "var(--bg-2)",
              color: "#fff", display: "grid", placeItems: "center",
              transition: "background .2s",
            }}>
              {idx < i ? <I.Check size={14}/> : idx === i ? <span className="spin" style={{ borderColor: "#fff", borderBottomColor: "transparent" }}/> : <span style={{ width: 8, height: 8, borderRadius: 50, background: "var(--ink-300)" }}/>}
            </span>
            <span style={{ fontWeight: idx === i ? 600 : 500, color: idx <= i ? "var(--ink-900)" : "var(--ink-500)" }}>{s.t}</span>
          </div>
        ))}
      </div>
      {submitError ? (
        <div className="cta-bar">
          <Button kind="primary" block onClick={() => nav("/review")}>Back to review</Button>
          <Button kind="secondary" block onClick={() => nav("/checkout")}>Secure checkout</Button>
        </div>
      ) : (
        <p className="subtle text-center" style={{ fontSize: 12 }}>
          Your packet is queued for internal review before any SDAT submission step.
        </p>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Submitted (magic-link confirmation)
// ─────────────────────────────────────────────────────────────────────────────
function SubmittedScreen({ nav, session }) {
  const statusToken = getStatusToken(session);
  const link = getStatusUrl(session);
  const [copied, setCopied] = useState(false);
  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 20, alignItems: "center", paddingTop: 24, textAlign: "center" }}>
      <div style={{ width: 72, height: 72, borderRadius: 18, background: "var(--good-100)", color: "var(--good-700)", display: "grid", placeItems: "center" }}>
        <I.CheckCircle size={36}/>
      </div>
      <div>
        <h2 className="h-page">Your Homestead packet is queued for review</h2>
        <p className="subtle" style={{ marginTop: 8, maxWidth: 48 + "ch" }}>
          We generated your packet and queued it for internal review. We've emailed your status link to <strong style={{ color: "var(--ink-700)" }}>{session.filing?.email}</strong>.
        </p>
      </div>

      <div className="card" style={{ width: "100%", textAlign: "left" }}>
        <h3 className="h-section">Your status link</h3>
        <p className="subtle" style={{ marginBottom: 12 }}>Bookmark this. There's no account or password — opening the link is how you check progress.</p>
        <pre className="codeblock" onClick={(e) => { navigator.clipboard?.writeText(link); setCopied(true); setTimeout(()=>setCopied(false), 1400); }} style={{ cursor: "copy" }}>
{link}
        </pre>
        <div className="row mt-3 status-link-actions">
          <Button kind="secondary" size="sm" icon={<I.Copy size={14}/>} onClick={() => { navigator.clipboard?.writeText(link); setCopied(true); setTimeout(()=>setCopied(false), 1400); }}>{copied ? "Copied!" : "Copy link"}</Button>
          <Button kind="primary" size="sm" iconRight={<I.ArrowRight size={14}/>} onClick={() => nav("/status/" + statusToken)}>Open status page</Button>
        </div>
      </div>

      <Callout kind="info" icon={<I.Info size={18}/>}>
        <strong>Note:</strong> Your packet goes through internal review before any SDAT submission step. We'll update your status page as soon as it moves forward.
      </Callout>

      <div className="rcpt" style={{ width: "100%", textAlign: "left" }}>
        <div className="row"><span className="k">Stripe checkout</span><span className="v" style={{ marginLeft: "auto" }}>On file with Stripe</span></div>
        <div className="row"><span className="k">Amount</span><span className="v" style={{ marginLeft: "auto" }}>{pFmtUSD(39.99, { cents: true })}</span></div>
        <div className="row"><span className="k">Filing ID</span><span className="v" style={{ marginLeft: "auto" }}>{session.filing_id}</span></div>
      </div>

      <p className="subtle" style={{ fontSize: 12 }}>EzClaim is not a government agency. You can also file free directly with SDAT at <a href="https://sdathtc.dat.maryland.gov" target="_blank" rel="noreferrer">sdathtc.dat.maryland.gov</a>.</p>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Status page (magic-link target)
// ─────────────────────────────────────────────────────────────────────────────
function StatusScreen({ nav, session, setSession, scenario = "happy", statusToken = "" }) {
  const [refreshing, setRefreshing] = useState(false);
  const [lastRefresh, setLastRefresh] = useState(new Date());
  const [loadingRecord, setLoadingRecord] = useState(!!statusToken);
  const [statusError, setStatusError] = useState("");

  useEffect(() => {
    if (!statusToken) return;
    let cancelled = false;
    setLoadingRecord(true);
    setStatusError("");

    fetch("/api/filings/" + encodeURIComponent(statusToken))
      .then((resp) => resp.ok ? resp.json() : Promise.reject(new Error("status_not_found")))
      .then((data) => {
        if (cancelled) return;
        setSession(mergeStatusRecord(session, data.filing));
      })
      .catch(() => {
        if (!cancelled) setStatusError("We could not find that status link. Check the URL from your email and try again.");
      })
      .finally(() => {
        if (!cancelled) setLoadingRecord(false);
      });

    return () => { cancelled = true; };
  }, [statusToken]);

  const missingStatus = !statusToken && !session.filing_id;

  const property = session?.lookup?.matches?.[0] || PROPERTIES[0];

  async function refreshFromRecords() {
    if (!property?.site_address) return;
    setRefreshing(true);
    try {
      const res = await pLookup(property.site_address);
      const fresh = res?.matches?.[0];
      if (fresh) {
        setSession({
          ...session,
          lookup: res,
          sdat_status: pHomesteadStatus(fresh).label,
        });
      }
    } catch (e) {
      // Keep the local status visible if the public-record refresh fails.
    } finally {
      setRefreshing(false);
      setLastRefresh(new Date());
    }
  }

  // Refresh-on-open
  useEffect(() => {
    if (!session.filing_id) return;
    refreshFromRecords();
  }, [session.filing_id]);

  if (missingStatus) {
    return (
      <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 18 }}>
        <Callout kind="err" icon={<I.Alert size={18}/>} title="Missing status link">
          Open the secure status link from your email, or return to review to continue a saved filing.
        </Callout>
        <div className="cta-bar">
          <Button kind="primary" block onClick={() => nav("/review")}>Back to review</Button>
          <Button kind="secondary" block onClick={() => nav("/")}>Start lookup</Button>
        </div>
      </div>
    );
  }

  const STATUS_LABELS = {
    paid_waiting_for_details: "Payment received",
    pending_review: "Filing packet pending review",
    fax_submitting: "Fax submission in progress",
    fax_submitted: "Fax submitted to SDAT",
    fax_retrying: "Fax status pending",
    fax_delivered: "Fax delivered to SDAT",
    fax_failed: "Fax needs attention",
    review_rejected: "Needs support",
  };
  const events = session.events || [];
  const demoStatus =
    scenario === "fax_failed" ? "fax_failed" :
    scenario === "denied" ? "review_rejected" :
    scenario === "approved" ? "fax_delivered" :
    "";
  const filingStatus = session.status || session.submission?.status || demoStatus || "pending_review";
  const sdat = session.sdat_status || STATUS_LABELS[filingStatus] || "Pending internal review";
  const reviewAt = pEventTime(events, "review_approved") || pEventTime(events, "review");
  const faxSubmittedAt = pEventTime(events, "fax_submitted");
  const faxStatusAt = pEventTime(events, "fax_status_");

  const sdatTitle = ["fax_submitting", "fax_submitted", "fax_retrying", "fax_delivered", "fax_failed"].includes(filingStatus)
    ? STATUS_LABELS[filingStatus]
    : "SDAT submission";
  const timeline = [
    { k: "payment", t: "Payment received", at: session.payment?.paid_at, state: paymentState(filingStatus) },
    { k: "build",   t: "Filing packet generated", at: pEventTime(events, "build") || session.submitted_at, state: packetState(filingStatus) },
    { k: "review",  t: "Internal review", at: reviewAt, state: reviewState(filingStatus), sub: reviewSubcopy(filingStatus) },
    { k: "sdat",    t: sdatTitle, at: faxStatusAt || faxSubmittedAt, state: faxState(filingStatus), sub: faxSubcopy(filingStatus) },
  ];

  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 18 }}>
      {loadingRecord ? (
        <Callout kind="info" icon={<span className="spin"/>}>Loading your filing status…</Callout>
      ) : null}
      {statusError ? (
        <Callout kind="err" icon={<I.Alert size={18}/>}>{statusError}</Callout>
      ) : null}
      <div className="row between">
        <div className="row gap-2">
          <Tag kind="info"><I.Lock size={11}/> Secure link</Tag>
          <span className="muted" style={{ fontSize: 12 }}>No login required</span>
        </div>
        <Button kind="ghost" size="sm" icon={<I.Refresh size={14}/>} onClick={refreshFromRecords} disabled={refreshing}>
          {refreshing ? <>Refreshing…</> : <>Refresh now</>}
        </Button>
      </div>

      <div>
        <h2 className="h-page">Your Homestead filing</h2>
        <div className="row gap-2 mt-2 wrap" style={{ rowGap: 4 }}>
          <span className="muted" style={{ fontSize: 13, whiteSpace: "nowrap" }}>Filing ID</span>
          <span className="tabnum" style={{ fontSize: 13, fontWeight: 600, whiteSpace: "nowrap" }}>{session.filing_id}</span>
          <span className="muted" style={{ fontSize: 13 }}>·</span>
          <span className="muted" style={{ fontSize: 13, whiteSpace: "nowrap" }}>Last checked {pFmtTime(lastRefresh)}</span>
        </div>
      </div>

      <PropertyCard p={property} compact/>

      {filingStatus === "fax_failed" ? (
        <Callout kind="warn"><strong>Fax needs attention.</strong> We'll retry the submission or contact you if SDAT needs a corrected packet.</Callout>
      ) : filingStatus === "review_rejected" ? (
        <Callout kind="err"><strong>This filing needs support.</strong> We found an issue during review and will contact you before any SDAT submission step.</Callout>
      ) : filingStatus === "fax_delivered" ? (
        <Callout kind="good"><strong>Fax delivered to SDAT.</strong> SDAT public records may still take weeks to update.</Callout>
      ) : filingStatus === "fax_submitted" || filingStatus === "fax_retrying" || filingStatus === "fax_submitting" ? (
        <Callout kind="info"><strong>Fax submitted to SDAT.</strong> We are waiting for provider delivery confirmation and SDAT public-record updates.</Callout>
      ) : filingStatus === "paid_waiting_for_details" ? (
        <Callout kind="info"><strong>Payment received.</strong> Finish the secure details step so we can generate your filing packet.</Callout>
      ) : (
        <Callout kind="info"><strong>Your filing packet is being reviewed.</strong> We review the first live filings before faxing to make sure the SDAT packet is complete and readable.</Callout>
      )}

      <div className="card">
        <h3 className="h-section">Timeline</h3>
        <div className="tl">
          {timeline.map((row) => (
            <div key={row.k} className={"tl-row " + (row.state || "")}>
              <div className="tl-mark">
                {row.state === "done" ? <I.Check size={14}/> :
                 row.state === "err"  ? <I.X size={14}/> :
                 row.state === "cur"  ? <span className="spin" style={{ width: 12, height: 12, borderColor: "#fff", borderBottomColor: "transparent" }}/> :
                 <span style={{ width: 8, height: 8, borderRadius: 50, background: "currentColor" }}/>}
              </div>
              <div>
                <div className="tl-title">{row.t}</div>
                {row.sub ? <div className="tl-meta">{row.sub}</div> : null}
              </div>
              <div className="tl-time">{row.at ? pFmtTime(row.at) + " · " + pFmtDate(row.at) : "—"}</div>
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <h3 className="h-section">Receipt</h3>
        <div className="rcpt">
          <div className="row"><span className="k">Amount</span><span className="v" style={{ marginLeft: "auto" }}>{pFmtUSD(39.99, { cents: true })}</span></div>
          <div className="row"><span className="k">Paid</span><span className="v" style={{ marginLeft: "auto" }}>{pFmtDate(session.payment?.paid_at)}</span></div>
          <div className="row"><span className="k">Checkout session</span><span className="v" style={{ marginLeft: "auto" }}>On file with Stripe</span></div>
        </div>
        <div className="row mt-3">
          <Button kind="link" size="sm" icon={<I.Print size={14}/>} onClick={() => window.print()}>Print</Button>
          <Button kind="link" size="sm" icon={<I.Mail size={14}/>}>Email receipt</Button>
        </div>
      </div>

      <p className="subtle text-center" style={{ fontSize: 12 }}>
        Questions? Email <a href="mailto:help@ezclaim.app">help@ezclaim.app</a>. EzClaim is not a government agency. The Homestead Tax Credit is administered by Maryland SDAT — you can also file directly at <a href="https://sdathtc.dat.maryland.gov" target="_blank" rel="noreferrer">sdathtc.dat.maryland.gov</a>.
      </p>
    </div>
  );
}

function pEventTime(events, keyOrPrefix) {
  const found = events.find((event) => (
    event.k === keyOrPrefix || (String(keyOrPrefix).endsWith("_") && String(event.k || "").startsWith(keyOrPrefix))
  ));
  return found?.at || null;
}

function paymentState(status) {
  return status ? "done" : "cur";
}

function packetState(status) {
  return status === "paid_waiting_for_details" ? "todo" : "done";
}

function reviewState(status) {
  if (status === "review_rejected") return "err";
  if (status === "pending_review") return "cur";
  if (status === "paid_waiting_for_details") return "todo";
  return "done";
}

function faxState(status) {
  if (status === "fax_failed") return "err";
  if (["fax_submitting", "fax_submitted", "fax_retrying"].includes(status)) return "cur";
  if (status === "fax_delivered") return "done";
  if (status === "review_rejected") return "err";
  return "todo";
}

function reviewSubcopy(status) {
  if (status === "review_rejected") return "Review found an issue that needs support";
  if (status === "pending_review") return "Queued for packet review before SDAT submission";
  if (status === "paid_waiting_for_details") return "Waiting on secure filing details";
  return "Packet reviewed";
}

function faxSubcopy(status) {
  if (status === "fax_failed") return "We will retry or contact you if SDAT needs a corrected packet";
  if (status === "fax_submitting") return "Provider submission is in progress";
  if (status === "fax_submitted") return "Waiting for provider delivery confirmation";
  if (status === "fax_retrying") return "Provider status is still pending";
  if (status === "fax_delivered") return "Waiting for SDAT public records to update";
  if (status === "review_rejected") return "No SDAT submission was made";
  return "Pending internal review";
}

Object.assign(window, {
  CheckoutScreen, SecureDetailsScreen, SubmittingScreen, SubmittedScreen, StatusScreen,
});
