// #checks — AP Task 5. The checks-to-print queue and print-run setup, per
// docs/superpowers/specs/2026-08-01-ap-design.md's check-printing section.
// Two states, both on this one screen (the print itself happens on a
// separate route, #checks/print-run, since assigning numbers is a real
// server action that shouldn't repeat on every re-render of this screen):
//   1. Queue — pick a money account, see every bill_payment still
//      method='check'/check_status='to_print' on that account (date, payee,
//      amount), select which ones to print (select-all by default).
//   2. Run setup — once "Print N checks" is clicked: pick the stock format
//      (company default preselected) and nudge the alignment offsets (mm,
//      persisted the moment the run actually prints — see checks.ts's POST
//      /api/c/checks/print, which saves format/offsetX/offsetY alongside
//      assigning numbers). "Preview" hands the account/payments/format/
//      offsets to #checks/print-run as route params.

import { useEffect, useMemo, useState } from "react";
import { useCompany } from "../lib/company";
import { useApiGet } from "../lib/useApiGet";
import { fmtMoney } from "../lib/money";
import { fmtDate } from "../lib/date";
import { ErrorBanner } from "../components/ErrorBanner";
import type { ApiAccount } from "../accounts/subtypes";
import { accountSections } from "../accounts/pickerGroups";
import type { NavTarget } from "../lib/useHashView";

interface QueuePayment {
  paymentId: string;
  date: string;
  vendorId: string;
  vendorName: string;
  amount: string;
  memo: string | null;
}

interface QueueResponse {
  accountId: string;
  accountName: string;
  nextCheckNo: string;
  payments: QueuePayment[];
}

interface CheckSettings {
  format: string;
  offsetX: string;
  offsetY: string;
  accounts: Array<{ accountId: string; name: string; nextCheckNo: string; highestUsed: string | null }>;
}

type CheckFormat = "three_per_page" | "voucher";

function isCheckFormat(v: string): v is CheckFormat {
  return v === "three_per_page" || v === "voucher";
}

/** Nudges an mm offset string by `delta`, formatted to 2 decimal places —
 *  offsets aren't money (CLAUDE.md §2 is about monetary values, not print
 *  alignment), so plain Number arithmetic is fine here, same rationale as
 *  server/src/checks.ts's own offsetSchema comment. */
function nudge(current: string, delta: number): string {
  const n = Number(current);
  const next = (Number.isFinite(n) ? n : 0) + delta;
  return next.toFixed(2);
}

export function ChecksScreen({ navigate }: { navigate: (target: NavTarget) => void }) {
  const { selectedCompanyId } = useCompany();
  const { data: accounts } = useApiGet<ApiAccount[]>("/api/c/accounts", selectedCompanyId);
  // Asset-restricted, standard sections (Chart Management Task 3) — checks
  // print from an asset account, same restriction as before, now with Bank
  // accounts (etc.) headers instead of one flat "money" list.
  const accountSectionList = useMemo(() => accountSections(accounts, ["asset"]), [accounts]);
  const {
    data: settings, loading: settingsLoading, error: settingsError, retry: retrySettings,
  } = useApiGet<CheckSettings>("/api/c/settings/checks", selectedCompanyId);

  const [accountId, setAccountId] = useState<string | null>(null);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [seededQueueFor, setSeededQueueFor] = useState<string | null>(null);
  const [runSetup, setRunSetup] = useState(false);
  const [format, setFormat] = useState<CheckFormat>("three_per_page");
  const [offsetX, setOffsetX] = useState("0.00");
  const [offsetY, setOffsetY] = useState("0.00");
  const [settingsSeeded, setSettingsSeeded] = useState(false);

  // Default the account to the first one available, once accounts load —
  // first section, first row. Bank accounts lead, so if the lowest-code
  // asset is untagged or non-bank this prefers a bank account over strict
  // code order — the right bias for a check-printing default.
  useEffect(() => {
    if (accountId === null && accountSectionList.length > 0) {
      setAccountId(accountSectionList[0]!.accounts[0]!.id);
    }
  }, [accountSectionList, accountId]);

  // Seed the format/offset controls from the company's saved defaults
  // exactly once — after that, they're the user's own edits for this run
  // (mirrors SettingsScreen.tsx's seededForCompany pattern, simplified since
  // this screen has no server round trip to re-seed after).
  useEffect(() => {
    if (settings && !settingsSeeded) {
      if (isCheckFormat(settings.format)) setFormat(settings.format);
      setOffsetX(settings.offsetX);
      setOffsetY(settings.offsetY);
      setSettingsSeeded(true);
    }
  }, [settings, settingsSeeded]);

  const queuePath = accountId ? `/api/c/checks/queue?accountId=${accountId}` : null;
  const { data: queue, loading: queueLoading, error: queueError, retry: retryQueue } =
    useApiGet<QueueResponse>(queuePath, selectedCompanyId);

  // Select every queued payment by default, exactly once per account load —
  // switching accounts re-seeds to "everything checked" again, same
  // select-all-by-default convention as ReceivePayment.tsx's open-invoice
  // rows. Guarding on `queue.accountId === accountId` (not just `!queue`) is
  // deliberate: when the user switches money accounts, `queue` can still
  // hold the PREVIOUS account's (still non-null) response for one extra
  // render — useApiGet's own effect clearing it to null hasn't committed
  // yet — so checking only "is there a queue" would seed from stale data for
  // the wrong account and never get another chance to (seededQueueFor would
  // already be marked done). Caught during this task's live browser
  // verification switching accounts back and forth, not just theoretical.
  useEffect(() => {
    if (!queue || !accountId || queue.accountId !== accountId || seededQueueFor === accountId) return;
    setSelected(new Set(queue.payments.map((p) => p.paymentId)));
    setSeededQueueFor(accountId);
    setRunSetup(false);
  }, [queue, accountId, seededQueueFor]);

  function changeAccount(id: string) {
    setAccountId(id);
    setSelected(new Set());
    setSeededQueueFor(null);
    setRunSetup(false);
  }

  function toggleOne(paymentId: string) {
    setSelected((s) => {
      const next = new Set(s);
      if (next.has(paymentId)) next.delete(paymentId);
      else next.add(paymentId);
      return next;
    });
  }

  function toggleAll() {
    if (!queue) return;
    setSelected((s) => (s.size === queue.payments.length ? new Set() : new Set(queue.payments.map((p) => p.paymentId))));
  }

  const selectedCount = selected.size;

  function goToPreview() {
    if (!accountId || selectedCount === 0) return;
    navigate({
      view: "checks-print",
      params: {
        accountId,
        paymentIds: Array.from(selected).join(","),
        format,
        offsetX,
        offsetY,
      },
    });
  }

  const allChecked = queue !== undefined && queue !== null && queue.payments.length > 0 && selectedCount === queue.payments.length;

  return (
    <div className="wrap">
      <header className="top">
        <h1>Checks to print</h1>
      </header>

      {settingsError && <ErrorBanner message={settingsError} onRetry={retrySettings} />}

      <div className="field" style={{ maxWidth: 360 }}>
        <label htmlFor="checks-account">Account</label>
        <select
          id="checks-account"
          className="select-input"
          value={accountId ?? ""}
          onChange={(ev) => changeAccount(ev.target.value)}
        >
          {accountSectionList.map((section) => (
            <optgroup key={section.label} label={section.label}>
              {section.accounts.map((a) => (
                <option key={a.id} value={a.id}>
                  {a.name}
                </option>
              ))}
            </optgroup>
          ))}
        </select>
      </div>

      {(!accounts || settingsLoading) && !queue && <p className="msg-inline">Loading&hellip;</p>}

      {accountId && queueError && <ErrorBanner message={queueError} onRetry={retryQueue} />}
      {accountId && queueLoading && !queue && <p className="msg-inline">Loading queued checks&hellip;</p>}

      {accountId && queue && queue.payments.length === 0 && (
        <div className="empty-state">
          No checks queued to print on {queue.accountName}. Pay a bill by check and choose "Print a check" to
          add one here.
        </div>
      )}

      {accountId && queue && queue.payments.length > 0 && !runSetup && (
        <>
          <div className="card" style={{ marginBottom: 18 }}>
            <table>
              <thead>
                <tr>
                  <th>
                    <input type="checkbox" checked={allChecked} onChange={toggleAll} aria-label="Select all" />
                  </th>
                  <th>Date</th>
                  <th>Payee</th>
                  <th className="num">Amount</th>
                </tr>
              </thead>
              <tbody>
                {queue.payments.map((p) => (
                  <tr key={p.paymentId}>
                    <td>
                      <input
                        type="checkbox"
                        checked={selected.has(p.paymentId)}
                        onChange={() => toggleOne(p.paymentId)}
                        aria-label={`Print check for ${p.vendorName}`}
                      />
                    </td>
                    <td>{fmtDate(p.date)}</td>
                    <td>{p.vendorName}</td>
                    <td className="num">{fmtMoney(p.amount)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>

          <div className="wizard-actions">
            <button type="button" className="btn" disabled={selectedCount === 0} onClick={() => setRunSetup(true)}>
              Print {selectedCount} check{selectedCount === 1 ? "" : "s"}
            </button>
          </div>
        </>
      )}

      {accountId && queue && runSetup && (
        <div className="card check-run-setup">
          <h2>Print {selectedCount} check{selectedCount === 1 ? "" : "s"}</h2>
          <p className="sub">
            Next check number on {queue.accountName}: {queue.nextCheckNo}.
          </p>

          <div className="field">
            <label>Check stock</label>
            <div className="method-choice">
              <label className="method-option">
                <input
                  type="radio"
                  name="check-format"
                  checked={format === "three_per_page"}
                  onChange={() => setFormat("three_per_page")}
                />
                <span>3 checks per page</span>
              </label>
              <label className="method-option">
                <input
                  type="radio"
                  name="check-format"
                  checked={format === "voucher"}
                  onChange={() => setFormat("voucher")}
                />
                <span>Voucher (check + 2 stubs)</span>
              </label>
            </div>
          </div>

          <div className="field-row">
            <div className="field offset-field">
              <label htmlFor="offset-x">Horizontal offset (mm)</label>
              <div className="offset-nudger">
                <button type="button" className="btn" onClick={() => setOffsetX((v) => nudge(v, -0.5))}>
                  &minus;
                </button>
                <input
                  id="offset-x"
                  className="text-input"
                  type="text"
                  inputMode="decimal"
                  value={offsetX}
                  onChange={(ev) => setOffsetX(ev.target.value)}
                />
                <button type="button" className="btn" onClick={() => setOffsetX((v) => nudge(v, 0.5))}>
                  +
                </button>
              </div>
            </div>
            <div className="field offset-field">
              <label htmlFor="offset-y">Vertical offset (mm)</label>
              <div className="offset-nudger">
                <button type="button" className="btn" onClick={() => setOffsetY((v) => nudge(v, -0.5))}>
                  &minus;
                </button>
                <input
                  id="offset-y"
                  className="text-input"
                  type="text"
                  inputMode="decimal"
                  value={offsetY}
                  onChange={(ev) => setOffsetY(ev.target.value)}
                />
                <button type="button" className="btn" onClick={() => setOffsetY((v) => nudge(v, 0.5))}>
                  +
                </button>
              </div>
            </div>
          </div>

          <div className="wizard-actions">
            <button type="button" className="btn" onClick={goToPreview}>
              Preview
            </button>
            <button type="button" className="acct-link" onClick={() => setRunSetup(false)}>
              Back to the queue
            </button>
          </div>
        </div>
      )}
    </div>
  );
}
