// #payment/:id — AR Task 6. Header (date, customer, amount,
// applied/unapplied), the applications this payment made (each one linking
// to its invoice), the posting/void entry links, a Void button (English
// confirm per the design brief: "The invoices it covered become open
// again."), and — when this payment still has unapplied money left over —
// an "Apply credit" mini-flow that reuses the same OpenInvoiceLinesTable
// ReceivePayment.tsx uses, just with a different ceiling: the invoice picker
// there caps against the amount the customer is PAYING right now; here it
// caps against the credit THIS payment already has sitting unapplied (no new
// money moves — apply_credit() posts nothing, per the design spec).

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 { OpenInvoiceLinesTable } from "./OpenInvoiceLinesTable";
import {
  buildApplications,
  centsOfNumeric,
  centsToEntered,
  lineAmountError,
  seedLines,
  sumCheckedCents,
  type OpenInvoiceApiRow,
  type OpenInvoiceLine,
} from "./openInvoiceLines";

interface PaymentApplication {
  id: string;
  invoiceId: string;
  invoiceNo: string | null;
  invoiceDate: string;
  dueDate: string;
  amount: string;
}

interface PaymentDetailData {
  paymentId: string;
  date: string;
  customerId: string;
  customerName: string;
  customerEmail: string | null;
  amount: string;
  applied: string;
  unapplied: string;
  status: string;
  memo: string | null;
  depositAccountId: string;
  depositAccountName: string;
  postedEntryId: string;
  voidEntryId: string | null;
  applications: PaymentApplication[];
}

interface OpenInvoicesResponse {
  invoices: OpenInvoiceApiRow[];
  unappliedCredit: string;
}

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

export function PaymentDetail({ paymentId, navigate }: { paymentId: string; navigate: (target: NavTarget) => void }) {
  const { selectedCompanyId } = useCompany();
  const path = `/api/c/payments/${paymentId}`;
  const { data, loading, error, retry } = useApiGet<PaymentDetailData>(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<OpenInvoiceLine[]>([]);
  const [applySubmitting, setApplySubmitting] = useState(false);
  const [applyError, setApplyError] = useState<string | null>(null);
  const [applySuccess, setApplySuccess] = useState<ApplyResult | null>(null);

  const openInvoicesPath =
    applying && data ? `/api/c/customers/${data.customerId}/open-invoices` : null;
  const { data: openInvoices, loading: openInvoicesLoading, error: openInvoicesError } =
    useApiGet<OpenInvoicesResponse>(openInvoicesPath, selectedCompanyId);

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

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

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

  function changeApplyAmount(invoiceId: string, value: string) {
    setApplyLines((ls) => ls.map((l) => (l.invoiceId === invoiceId ? { ...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/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/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.customerName} · {fmtDate(data.date)} · into {data.depositAccountName}
          </div>

          <div className="card" style={{ margin: "18px 0" }}>
            <div className="rows">
              <div className="row">
                <span className="n">Amount received</span>
                <span className="a">{fmtMoney(data.amount)}</span>
              </div>
              <div className="row">
                <span className="n">Applied to invoices</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>

          {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 invoice — kept entirely as customer credit.</p>
            ) : (
              <ul className="entry-lines">
                {data.applications.map((a) => (
                  <li key={a.id}>
                    <button type="button" className="acct-link" onClick={() => navigate({ view: "invoice", id: a.invoiceId })}>
                      {a.invoiceNo ? `Invoice #${a.invoiceNo}` : "Draft invoice"}
                    </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 an invoice
                </button>
              ) : (
                <>
                  {applyError && <ErrorBanner message={applyError} onRetry={() => void handleApply()} />}
                  {openInvoicesError && <ErrorBanner message={openInvoicesError} onRetry={() => setApplying(false)} />}
                  {openInvoicesLoading && !openInvoices && <p className="msg-inline">Loading open invoices&hellip;</p>}

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

                  {applyLines.length > 0 && (
                    <>
                      <OpenInvoiceLinesTable 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 invoices it covered become open again."
                      : "This removes the customer credit it created."}
                  </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>
  );
}
