// #invoice/:id — AR Task 5 item 4. Header (number or "Draft", state chip,
// dates, customer), lines table, totals, applications (plain rows for now —
// Task 6 links these to payments), entry links (posted/void) to #entry/:id,
// and the action set: Edit + Issue (drafts only), Void (issued invoices,
// with an inline English confirm — the API's own "refused: has payments"
// message surfaces verbatim on failure, per the design brief), Print
// (issued invoices only, i.e. once a number has been assigned).

import { 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, isZeroMoney } from "../lib/money";
import { trimPercent } from "../lib/percent";
import { ErrorBanner } from "../components/ErrorBanner";
import { BackLink } from "../components/BackLink";
import type { NavTarget } from "../lib/useHashView";
import { InvoiceForm, type InvoiceFormInitial } from "./InvoiceForm";

interface InvoiceLine {
  id: string;
  description: string | null;
  quantity: string;
  unitPrice: string;
  amount: string;
  incomeAccountId: string;
  incomeAccountName: string;
  taxable: boolean;
  // Inventory Task 4 — null on every line that doesn't name an item.
  itemId: string | null;
  itemName: string | null;
  itemKind: string | null;
}

interface InvoiceApplication {
  id: string;
  paymentId: string;
  amount: string;
  paymentDate: string;
  paymentStatus: string;
}

interface InvoiceDetailData {
  invoiceId: string;
  invoiceNo: string | null;
  status: string;
  state: "draft" | "open" | "partial" | "paid" | "void";
  overdue: boolean;
  invoiceDate: string;
  dueDate: string;
  memo: string | null;
  documentOnly: boolean;
  postedEntryId: string | null;
  voidEntryId: string | null;
  customer: {
    id: string;
    name: string;
    email: string | null;
    phone: string | null;
    address: string | null;
    termsDays: number;
    taxExempt: boolean;
  };
  lines: InvoiceLine[];
  applications: InvoiceApplication[];
  // Sales tax breakdown (0015) — taxRate is null and taxAmount "0.0000" on
  // every untaxed invoice, in which case the totals render exactly as they
  // did before the feature existed (invisible when unused).
  subtotal: string;
  taxAmount: string;
  taxRate: { id: string; name: string; ratePercent: string } | null;
  total: string;
  applied: string;
  open: string;
}

const STATE_LABEL: Record<InvoiceDetailData["state"], string> = {
  draft: "Draft",
  open: "Open",
  partial: "Partly paid",
  paid: "Paid",
  void: "Void",
};

const STATE_CHIP_CLASS: Record<InvoiceDetailData["state"], string> = {
  draft: "chip chip-muted",
  open: "chip chip-info",
  partial: "chip chip-warn",
  paid: "chip",
  void: "chip chip-muted",
};

/** "1.0000" -> "1", "0.5000" -> "0.5" — quantity isn't money (CLAUDE.md §2 is
 * about monetary values), so plain string trimming is fine here. */
function fmtQuantity(q: string): string {
  if (!q.includes(".")) return q;
  return q.replace(/0+$/, "").replace(/\.$/, "");
}

export function InvoiceDetail({
  invoiceId,
  navigate,
}: {
  invoiceId: string;
  navigate: (target: NavTarget) => void;
}) {
  const { selectedCompanyId } = useCompany();
  const path = `/api/c/invoices/${invoiceId}`;
  const { data, loading, error, retry } = useApiGet<InvoiceDetailData>(path, selectedCompanyId);

  const [editing, setEditing] = useState(false);
  const [issuing, setIssuing] = useState(false);
  const [issueError, setIssueError] = useState<string | null>(null);
  const [voidConfirming, setVoidConfirming] = useState(false);
  const [voiding, setVoiding] = useState(false);
  const [voidError, setVoidError] = useState<string | null>(null);

  async function handleIssue() {
    if (!selectedCompanyId) return;
    setIssuing(true);
    setIssueError(null);
    try {
      await apiPost(`/api/c/invoices/${invoiceId}/issue`, undefined, selectedCompanyId);
      retry();
    } catch (err) {
      setIssueError(err instanceof ApiError ? err.message : "Couldn't issue that invoice. Try again.");
    } finally {
      setIssuing(false);
    }
  }

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

  const initialForEdit: InvoiceFormInitial | null = data
    ? {
        customerId: data.customer.id,
        invoiceDate: data.invoiceDate,
        dueDate: data.dueDate,
        memo: data.memo ?? "",
        taxRateId: data.taxRate?.id ?? null,
        lines: data.lines.map((l) => ({
          description: l.description ?? "",
          quantity: fmtQuantity(l.quantity),
          unitPrice: l.unitPrice.replace(/0+$/, "").replace(/\.$/, "") || l.unitPrice,
          incomeAccountId: l.incomeAccountId,
          taxable: l.taxable,
          itemId: l.itemId,
        })),
      }
    : null;

  // Only an invoice that actually names an item grows an Item column.
  const hasItemLines = (data?.lines ?? []).some((l) => l.itemId !== null);

  const hasTax = data !== null && data.taxRate !== null && !isZeroMoney(data.taxAmount);

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

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

      {data && !editing && (
        <>
          <header className="top">
            <h1>{data.invoiceNo ? `Invoice #${data.invoiceNo}` : "Draft invoice"}</h1>
            <span className={STATE_CHIP_CLASS[data.state]}>{STATE_LABEL[data.state]}</span>
            {data.overdue && <span className="chip chip-bad">Overdue</span>}
          </header>
          <div className="asof">
            {data.customer.name} · Invoice date {fmtDate(data.invoiceDate)} · Due {fmtDate(data.dueDate)}
          </div>

          {data.documentOnly && (
            <p className="badge" style={{ marginTop: 10, display: "inline-flex" }}>
              No accounting impact — already in your books
            </p>
          )}

          <div className="card" style={{ margin: "18px 0" }}>
            <h2>Lines</h2>
            <table>
              <thead>
                <tr>
                  {hasItemLines && <th>Item</th>}
                  <th>Description</th>
                  <th className="num">Qty</th>
                  <th className="num">Unit price</th>
                  <th className="num">Amount</th>
                  <th>Category</th>
                </tr>
              </thead>
              <tbody>
                {data.lines.map((l) => (
                  <tr key={l.id}>
                    {hasItemLines && (
                      <td>
                        {l.itemId ? (
                          <button
                            type="button"
                            className="acct-link"
                            onClick={() => navigate({ view: "item", id: l.itemId! })}
                          >
                            {l.itemName}
                          </button>
                        ) : (
                          "—"
                        )}
                      </td>
                    )}
                    <td>{l.description ?? "—"}</td>
                    <td className="num">{fmtQuantity(l.quantity)}</td>
                    <td className="num">{fmtMoney(l.unitPrice)}</td>
                    <td className="num">{fmtMoney(l.amount)}</td>
                    <td>{l.incomeAccountName}</td>
                  </tr>
                ))}
              </tbody>
            </table>

            <div className="rows" style={{ marginTop: 12 }}>
              {hasTax && data.taxRate && (
                <>
                  <div className="row">
                    <span className="n">Subtotal</span>
                    <span className="a">{fmtMoney(data.subtotal)}</span>
                  </div>
                  <div className="row">
                    <span className="n">
                      {data.taxRate.name} ({trimPercent(data.taxRate.ratePercent)}%)
                    </span>
                    <span className="a">{fmtMoney(data.taxAmount)}</span>
                  </div>
                </>
              )}
              <div className="row">
                <span className="n">Total</span>
                <span className="a">{fmtMoney(data.total)}</span>
              </div>
              <div className="row">
                <span className="n">Applied</span>
                <span className="a">{fmtMoney(data.applied)}</span>
              </div>
              <div className="row total">
                <span className="n">Open</span>
                <span className="a">{fmtMoney(data.open)}</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>Payments applied</h2>
            {data.applications.length === 0 ? (
              <p className="sub">No payments applied yet.</p>
            ) : (
              <ul className="entry-lines">
                {data.applications.map((a) => (
                  <li key={a.id}>
                    <button type="button" className="acct-link" onClick={() => navigate({ view: "payment", id: a.paymentId })}>
                      {fmtDate(a.paymentDate)} — {fmtMoney(a.amount)} applied
                    </button>
                    {a.paymentStatus === "void" && " (payment voided)"}
                  </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>
          )}

          <div className="card">
            <h2>Actions</h2>
            {issueError && <ErrorBanner message={issueError} onRetry={() => void handleIssue()} />}
            {voidError && <ErrorBanner message={voidError} onRetry={() => void handleVoid()} />}

            {!voidConfirming ? (
              <div className="wizard-actions">
                {data.state === "draft" && (
                  <>
                    <button type="button" className="btn" onClick={() => setEditing(true)}>
                      Edit
                    </button>
                    <button type="button" className="btn" disabled={issuing} onClick={() => void handleIssue()}>
                      {issuing ? "Issuing…" : "Issue"}
                    </button>
                  </>
                )}
                {(data.state === "open" || data.state === "partial") && (
                  <button
                    type="button"
                    className="btn"
                    onClick={() =>
                      navigate({
                        view: "receive-payment",
                        params: { customerId: data.customer.id, invoiceId: data.invoiceId },
                      })
                    }
                  >
                    Receive payment
                  </button>
                )}
                {data.status === "open" && (
                  <button type="button" className="btn" onClick={() => setVoidConfirming(true)}>
                    Void
                  </button>
                )}
                {data.invoiceNo && (
                  <button
                    type="button"
                    className="btn"
                    onClick={() => navigate({ view: "invoice-print", id: invoiceId })}
                  >
                    Print
                  </button>
                )}
              </div>
            ) : (
              <div className="confirm-panel">
                <p>
                  This voids invoice #{data.invoiceNo} — it will no longer count toward{" "}
                  {data.customer.name}&rsquo;s balance. If it already has payments applied, voiding is
                  refused until those are undone.
                </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>
            )}
            {data.state === "draft" && <p className="invoice-form-drafts-note">Drafts stay until issued or edited.</p>}
          </div>
        </>
      )}

      {data && editing && initialForEdit && (
        <InvoiceForm
          mode="edit"
          invoiceId={invoiceId}
          initial={initialForEdit}
          onCancel={() => setEditing(false)}
          onSaved={() => {
            setEditing(false);
            retry();
          }}
        />
      )}
    </div>
  );
}
