// #bill-payment/:id — AP Task 5. Mirrors payments/PaymentDetail.tsx: header
// (date, vendor, amount, applied/unapplied), the applications this payment
// made (each one linking to its bill), the posting/void entry links, a Void
// button (English confirm per the design brief: "The bills it covered become
// open again." — plus, for a check that already carries a number, a note
// that the number stays consumed, never reused), and — when this payment
// still has unapplied money left over — an "Apply credit" mini-flow that
// reuses the same OpenBillLinesTable PayBills.tsx uses. The one thing with
// no AR analogue: a "Check" card showing the payment method, the check
// number (or "Queued to print" while it's still waiting), and the printed
// date once it's been run through a print job.

import { useEffect, useState } from "react";
import { useCompany } from "../lib/company";
import { useApiGet } from "../lib/useApiGet";
import { apiPost, ApiError } from "../lib/api";
import { fmtDate, todayIso } from "../lib/date";
import { fmtMoney } from "../lib/money";
import { ErrorBanner } from "../components/ErrorBanner";
import { BackLink } from "../components/BackLink";
import type { NavTarget } from "../lib/useHashView";
import { OpenBillLinesTable } from "./OpenBillLinesTable";
import {
  buildApplications,
  centsOfNumeric,
  centsToEntered,
  lineAmountError,
  seedLines,
  sumCheckedCents,
  type OpenBillApiRow,
  type OpenBillLine,
} from "./openBillLines";

interface PaymentApplication {
  id: string;
  billId: string;
  refNo: string | null;
  billDate: string;
  dueDate: string;
  amount: string;
}

interface BillPaymentDetailData {
  paymentId: string;
  date: string;
  vendorId: string;
  vendorName: string;
  vendorEmail: string | null;
  amount: string;
  applied: string;
  unapplied: string;
  status: string;
  memo: string | null;
  method: string;
  checkNo: string | null;
  checkStatus: string | null;
  printedOn: string | null;
  moneyAccountId: string;
  moneyAccountName: string;
  postedEntryId: string;
  voidEntryId: string | null;
  applications: PaymentApplication[];
}

interface OpenBillsResponse {
  bills: OpenBillApiRow[];
  unappliedCredit: string;
}

interface ApplyResult {
  paymentId: string;
  summary: string;
}

function checkStatusLabel(checkStatus: string | null): string {
  if (checkStatus === "to_print") return "Queued to print";
  if (checkStatus === "printed") return "Printed";
  if (checkStatus === "manual") return "Recorded";
  if (checkStatus === "void") return "Void";
  return "—";
}

export function BillPaymentDetail({
  paymentId,
  navigate,
}: {
  paymentId: string;
  navigate: (target: NavTarget) => void;
}) {
  const { selectedCompanyId } = useCompany();
  const path = `/api/c/bill-payments/${paymentId}`;
  const { data, loading, error, retry } = useApiGet<BillPaymentDetailData>(path, selectedCompanyId);

  const [voidConfirming, setVoidConfirming] = useState(false);
  const [voiding, setVoiding] = useState(false);
  const [voidError, setVoidError] = useState<string | null>(null);

  const [applying, setApplying] = useState(false);
  const [applyLines, setApplyLines] = useState<OpenBillLine[]>([]);
  const [applySubmitting, setApplySubmitting] = useState(false);
  const [applyError, setApplyError] = useState<string | null>(null);
  const [applySuccess, setApplySuccess] = useState<ApplyResult | null>(null);

  const openBillsPath = applying && data ? `/api/c/vendors/${data.vendorId}/open-bills` : null;
  const { data: openBills, loading: openBillsLoading, error: openBillsError } =
    useApiGet<OpenBillsResponse>(openBillsPath, selectedCompanyId);

  function startApplying() {
    setApplying(true);
    setApplyLines([]);
    setApplySuccess(null);
    setApplyError(null);
  }

  // Seed the mini-flow's rows the first time this payment's vendor's open
  // bills load — every row unchecked (see openBillLines.ts's seedLines doc
  // comment for why apply-credit's default differs from PayBills.tsx's).
  useEffect(() => {
    if (!applying || !openBills || applyLines.length > 0) return;
    setApplyLines(seedLines(openBills.bills, { allUnchecked: true }));
  }, [applying, openBills, applyLines.length]);

  function toggleApplyLine(billId: string) {
    setApplyLines((ls) => ls.map((l) => (l.billId === billId ? { ...l, checked: !l.checked } : l)));
  }

  function changeApplyAmount(billId: string, value: string) {
    setApplyLines((ls) => ls.map((l) => (l.billId === billId ? { ...l, amount: value } : l)));
  }

  const creditCents = data ? centsOfNumeric(data.unapplied) : 0n;
  const appliedCents = sumCheckedCents(applyLines);
  const linesValid = applyLines.every((l) => lineAmountError(l) === null);
  const overCredit = appliedCents > creditCents;
  const anyChecked = applyLines.some((l) => l.checked && l.amount.trim() !== "");
  const applyFormValid = linesValid && !overCredit && anyChecked;

  async function handleApply() {
    if (!selectedCompanyId || !applyFormValid) return;
    setApplySubmitting(true);
    setApplyError(null);
    try {
      const result = await apiPost<ApplyResult>(
        `/api/c/bill-payments/${paymentId}/apply`,
        { applications: buildApplications(applyLines) },
        selectedCompanyId,
      );
      setApplySuccess(result);
      setApplying(false);
      setApplyLines([]);
      retry();
    } catch (err) {
      setApplyError(err instanceof ApiError ? err.message : "Couldn't apply that credit. Try again.");
    } finally {
      setApplySubmitting(false);
    }
  }

  async function handleVoid() {
    if (!selectedCompanyId) return;
    setVoiding(true);
    setVoidError(null);
    try {
      await apiPost(`/api/c/bill-payments/${paymentId}/void`, { date: todayIso() }, selectedCompanyId);
      setVoidConfirming(false);
      retry();
    } catch (err) {
      setVoidError(err instanceof ApiError ? err.message : "Couldn't void that payment. Try again.");
    } finally {
      setVoiding(false);
    }
  }

  const hadApplications = (data?.applications.length ?? 0) > 0;

  return (
    <div className="wrap">
      <BackLink label="Back" />

      {error && <ErrorBanner message={error} onRetry={retry} />}
      {loading && !data && <p className="msg-inline">Loading&hellip;</p>}

      {data && (
        <>
          <header className="top">
            <h1>Payment of {fmtMoney(data.amount)}</h1>
            <span className={data.status === "void" ? "chip chip-muted" : "chip"}>
              {data.status === "void" ? "Voided" : "Active"}
            </span>
          </header>
          <div className="asof">
            {data.vendorName} · {fmtDate(data.date)} · from {data.moneyAccountName}
          </div>

          <div className="card" style={{ margin: "18px 0" }}>
            <div className="rows">
              <div className="row">
                <span className="n">Amount paid</span>
                <span className="a">{fmtMoney(data.amount)}</span>
              </div>
              <div className="row">
                <span className="n">Applied to bills</span>
                <span className="a">{fmtMoney(data.applied)}</span>
              </div>
              <div className="row total">
                <span className="n">Unapplied (credit)</span>
                <span className="a">{fmtMoney(data.unapplied)}</span>
              </div>
            </div>
          </div>

          <div className="card" style={{ marginBottom: 18 }}>
            <h2>Check</h2>
            <div className="rows">
              <div className="row">
                <span className="n">Method</span>
                <span className="a">{data.method === "check" ? "Check" : "Electronic"}</span>
              </div>
              {data.method === "check" && (
                <>
                  <div className="row">
                    <span className="n">Status</span>
                    <span className="a">{checkStatusLabel(data.checkStatus)}</span>
                  </div>
                  <div className="row">
                    <span className="n">Check number</span>
                    <span className="a">{data.checkNo ?? "Not yet assigned"}</span>
                  </div>
                  {data.printedOn && (
                    <div className="row">
                      <span className="n">Printed</span>
                      <span className="a">{fmtDate(data.printedOn)}</span>
                    </div>
                  )}
                </>
              )}
            </div>
          </div>

          {data.memo && (
            <div className="card" style={{ marginBottom: 18 }}>
              <h2>Memo</h2>
              <p>{data.memo}</p>
            </div>
          )}

          <div className="card" style={{ marginBottom: 18 }}>
            <h2>Applied to</h2>
            {data.applications.length === 0 ? (
              <p className="sub">Not applied to any bill — kept entirely as vendor credit.</p>
            ) : (
              <ul className="entry-lines">
                {data.applications.map((a) => (
                  <li key={a.id}>
                    <button type="button" className="acct-link" onClick={() => navigate({ view: "bill", id: a.billId })}>
                      {a.refNo ? `Bill #${a.refNo}` : "Bill (no reference)"}
                    </button>
                    {" — "}
                    {fmtMoney(a.amount)} applied {fmtDate(a.dueDate)} due
                  </li>
                ))}
              </ul>
            )}
          </div>

          {(data.postedEntryId || data.voidEntryId) && (
            <div className="card" style={{ marginBottom: 18 }}>
              <h2>In your books</h2>
              <div className="reversal-links">
                {data.postedEntryId && (
                  <button type="button" className="acct-link" onClick={() => navigate({ view: "entry", id: data.postedEntryId })}>
                    See how this was recorded
                  </button>
                )}
                {data.voidEntryId && (
                  <button type="button" className="acct-link" onClick={() => navigate({ view: "entry", id: data.voidEntryId! })}>
                    See the correcting entry
                  </button>
                )}
              </div>
            </div>
          )}

          {data.status === "active" && data.unapplied !== "0.0000" && (
            <div className="card" style={{ marginBottom: 18 }}>
              <h2>Apply credit</h2>
              {applySuccess && <p className="sub">{applySuccess.summary}</p>}
              {!applying ? (
                <button type="button" className="btn" onClick={startApplying}>
                  Apply this credit to a bill
                </button>
              ) : (
                <>
                  {applyError && <ErrorBanner message={applyError} onRetry={() => void handleApply()} />}
                  {openBillsError && <ErrorBanner message={openBillsError} onRetry={() => setApplying(false)} />}
                  {openBillsLoading && !openBills && <p className="msg-inline">Loading open bills&hellip;</p>}

                  {openBills && openBills.bills.length === 0 && (
                    <p className="sub">This vendor has no other open bills to apply credit to.</p>
                  )}

                  {applyLines.length > 0 && (
                    <>
                      <OpenBillLinesTable lines={applyLines} onToggle={toggleApplyLine} onAmountChange={changeApplyAmount} />
                      <p className="sub">
                        Applying ${centsToEntered(appliedCents)} of ${centsToEntered(creditCents)} available credit
                        {overCredit ? " — that's more credit than this payment has left." : "."}
                      </p>
                    </>
                  )}

                  <div className="wizard-actions">
                    <button
                      type="button"
                      className="btn"
                      disabled={!applyFormValid || applySubmitting}
                      onClick={() => void handleApply()}
                    >
                      {applySubmitting ? "Applying…" : "Apply credit"}
                    </button>
                    <button type="button" className="acct-link" onClick={() => setApplying(false)}>
                      Cancel
                    </button>
                  </div>
                </>
              )}
            </div>
          )}

          {data.status === "active" && (
            <div className="card">
              <h2>Actions</h2>
              {voidError && <ErrorBanner message={voidError} onRetry={() => void handleVoid()} />}
              {!voidConfirming ? (
                <div className="wizard-actions">
                  <button type="button" className="btn" onClick={() => setVoidConfirming(true)}>
                    Void
                  </button>
                </div>
              ) : (
                <div className="confirm-panel">
                  <p>
                    {hadApplications
                      ? "The bills it covered become open again."
                      : "This removes the vendor credit it created."}
                    {data.method === "check" && data.checkNo !== null && (
                      <> Check #{data.checkNo} will become void and won't be reused.</>
                    )}
                  </p>
                  <div className="wizard-actions">
                    <button type="button" className="btn" disabled={voiding} onClick={() => void handleVoid()}>
                      {voiding ? "Voiding…" : "Yes, void it"}
                    </button>
                    <button type="button" className="acct-link" disabled={voiding} onClick={() => setVoidConfirming(false)}>
                      Cancel
                    </button>
                  </div>
                </div>
              )}
            </div>
          )}
        </>
      )}
    </div>
  );
}
