// #customers — AR Task 5 item 2. A plain table (name, contact, terms,
// balance, oldest due), sorted by name (the API already returns it that
// way — server/src/customers.ts's GET orders by c.name). "Add customer"
// and row-click-to-edit both open the SAME inline panel (CustomerPanel
// below) — this mirrors the Record screen's single-form-multiple-entry-
// points pattern rather than inventing a second layout for editing.
//
// A customer's balance is a link to #invoices pre-filtered to that customer
// (per the design brief: "balances link to #invoices filtered by that
// customer") via the sessionStorage handoff in customerFilterHandoff.ts —
// see that file's header for why this isn't a hash query param instead.

import { useState } from "react";
import { useCompany } from "../lib/company";
import { useApiGet } from "../lib/useApiGet";
import { apiPost, apiPatch, ApiError } from "../lib/api";
import { fmtMoney, isZeroMoney } from "../lib/money";
import { fmtDate } from "../lib/date";
import { ErrorBanner } from "../components/ErrorBanner";
import type { NavTarget } from "../lib/useHashView";
import { setPendingCustomerFilter } from "../invoices/customerFilterHandoff";

interface CustomerRow {
  id: string;
  name: string;
  email: string | null;
  phone: string | null;
  address: string | null;
  termsDays: number;
  active: boolean;
  balance: string;
  oldestOpenDueDate: string | null;
  taxExempt: boolean;
  exemptionNote: string | null;
}

// Sales tax gate (Task 3 of docs/superpowers/plans/2026-08-02-sales-tax.md):
// the exempt checkbox/note and the list's "(tax-exempt)" marker only exist
// for companies with sales tax ON — everyone else sees this screen exactly
// as it was before the feature existed.
interface SalesTaxSettings {
  enabled: boolean;
  accountId: string | null;
  accountName: string | null;
}

interface CustomerFormState {
  name: string;
  email: string;
  phone: string;
  address: string;
  termsDays: string;
  active: boolean;
  taxExempt: boolean;
  exemptionNote: string;
}

const BLANK_FORM: CustomerFormState = {
  name: "",
  email: "",
  phone: "",
  address: "",
  termsDays: "30",
  active: true,
  taxExempt: false,
  exemptionNote: "",
};

function toFormState(row: CustomerRow): CustomerFormState {
  return {
    name: row.name,
    email: row.email ?? "",
    phone: row.phone ?? "",
    address: row.address ?? "",
    termsDays: String(row.termsDays),
    active: row.active,
    taxExempt: row.taxExempt,
    exemptionNote: row.exemptionNote ?? "",
  };
}

// Loose client-side check only — the server is the real validator
// (customers.ts's nameSchema/emailSchema/termsDaysSchema); this just avoids a
// round trip for the obviously-empty case and gives an immediate English
// hint for a malformed terms number, matching the codebase's existing
// pattern (Record.tsx's amountError).
function validateForm(form: CustomerFormState): string | null {
  if (form.name.trim() === "") return "A customer needs a name.";
  if (form.termsDays.trim() !== "" && !/^\d+$/.test(form.termsDays.trim())) {
    return "Terms should be a whole number of days.";
  }
  return null;
}

function buildBody(form: CustomerFormState): Record<string, unknown> {
  return {
    name: form.name.trim(),
    email: form.email.trim() || undefined,
    phone: form.phone.trim() || undefined,
    address: form.address.trim() || undefined,
    termsDays: form.termsDays.trim() === "" ? undefined : Number(form.termsDays.trim()),
    // ALWAYS sent, even while sales tax is off (only the FIELDS are hidden
    // then — the values still ride from the seeded form state): the server's
    // PATCH replaces exemption_note unconditionally (absent -> NULL), so
    // omitting these on a disabled company would silently wipe a note stored
    // back when tax was on. tax_exempt round-trips the same way.
    taxExempt: form.taxExempt,
    exemptionNote: form.exemptionNote.trim() || undefined,
  };
}

function CustomerPanel({
  title,
  initial,
  showActiveToggle,
  showTaxFields,
  onCancel,
  onSave,
}: {
  title: string;
  initial: CustomerFormState;
  showActiveToggle: boolean;
  showTaxFields: boolean;
  onCancel: () => void;
  onSave: (form: CustomerFormState) => Promise<void>;
}) {
  const [form, setForm] = useState<CustomerFormState>(initial);
  const [touched, setTouched] = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const validationError = touched ? validateForm(form) : null;

  async function handleSave() {
    setTouched(true);
    const err = validateForm(form);
    if (err) return;
    setSaving(true);
    setError(null);
    try {
      await onSave(form);
    } catch (e) {
      setError(e instanceof ApiError ? e.message : "Couldn't save that customer. Try again.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <div className="card" style={{ marginBottom: 18, maxWidth: 480 }}>
      <h2>{title}</h2>
      {error && <ErrorBanner message={error} onRetry={() => void handleSave()} />}

      <div className="field">
        <label htmlFor="cust-name">Name</label>
        <input
          id="cust-name"
          className="text-input"
          type="text"
          value={form.name}
          onChange={(ev) => setForm((f) => ({ ...f, name: ev.target.value }))}
        />
      </div>
      <div className="field">
        <label htmlFor="cust-email">Email (optional)</label>
        <input
          id="cust-email"
          className="text-input"
          type="text"
          value={form.email}
          onChange={(ev) => setForm((f) => ({ ...f, email: ev.target.value }))}
        />
      </div>
      <div className="field">
        <label htmlFor="cust-phone">Phone (optional)</label>
        <input
          id="cust-phone"
          className="text-input"
          type="text"
          value={form.phone}
          onChange={(ev) => setForm((f) => ({ ...f, phone: ev.target.value }))}
        />
      </div>
      <div className="field">
        <label htmlFor="cust-address">Address (optional)</label>
        <input
          id="cust-address"
          className="text-input"
          type="text"
          value={form.address}
          onChange={(ev) => setForm((f) => ({ ...f, address: ev.target.value }))}
        />
      </div>
      <div className="field">
        <label htmlFor="cust-terms">Payment terms (days, optional — default 30)</label>
        <input
          id="cust-terms"
          className="text-input"
          type="text"
          inputMode="numeric"
          value={form.termsDays}
          onChange={(ev) => setForm((f) => ({ ...f, termsDays: ev.target.value }))}
        />
      </div>
      {showTaxFields && (
        <>
          <label className="field" style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
            <input
              type="checkbox"
              checked={form.taxExempt}
              onChange={(ev) => setForm((f) => ({ ...f, taxExempt: ev.target.checked }))}
            />
            <span>This customer is tax-exempt</span>
          </label>
          {form.taxExempt && (
            <div className="field">
              <label htmlFor="cust-exemption-note">
                Exemption note (optional — certificate number, reason)
              </label>
              <input
                id="cust-exemption-note"
                className="text-input"
                type="text"
                value={form.exemptionNote}
                onChange={(ev) => setForm((f) => ({ ...f, exemptionNote: ev.target.value }))}
              />
            </div>
          )}
        </>
      )}
      {showActiveToggle && (
        <label className="field" style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
          <input
            type="checkbox"
            checked={form.active}
            onChange={(ev) => setForm((f) => ({ ...f, active: ev.target.checked }))}
          />
          <span>Active</span>
        </label>
      )}
      {validationError && <p className="field-error">{validationError}</p>}

      <div className="wizard-actions">
        <button type="button" className="btn" disabled={saving} onClick={() => void handleSave()}>
          {saving ? "Saving…" : "Save"}
        </button>
        <button type="button" className="acct-link" disabled={saving} onClick={onCancel}>
          Cancel
        </button>
      </div>
    </div>
  );
}

type PanelState = { open: false } | { open: true; customer: CustomerRow | null };

export function CustomersScreen({ navigate }: { navigate: (target: NavTarget) => void }) {
  const { selectedCompanyId } = useCompany();
  const { data, loading, error, retry } = useApiGet<CustomerRow[]>("/api/c/customers", selectedCompanyId);
  const { data: taxSettings } = useApiGet<SalesTaxSettings>(
    "/api/c/settings/sales-tax", selectedCompanyId,
  );
  const taxEnabled = taxSettings?.enabled === true;
  const [panel, setPanel] = useState<PanelState>({ open: false });

  async function handleCreate(form: CustomerFormState) {
    if (!selectedCompanyId) return;
    await apiPost("/api/c/customers", buildBody(form), selectedCompanyId);
    setPanel({ open: false });
    retry();
  }

  async function handleUpdate(id: string, form: CustomerFormState) {
    if (!selectedCompanyId) return;
    await apiPatch(
      `/api/c/customers/${id}`,
      { ...buildBody(form), active: form.active },
      selectedCompanyId,
    );
    setPanel({ open: false });
    retry();
  }

  function goToCustomerInvoices(customerId: string) {
    setPendingCustomerFilter(customerId);
    navigate("invoices");
  }

  return (
    <div className="wrap">
      <header className="top">
        <h1>Customers</h1>
        {!panel.open && (
          <button type="button" className="btn" onClick={() => setPanel({ open: true, customer: null })}>
            Add customer
          </button>
        )}
      </header>

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

      {panel.open && (
        <CustomerPanel
          title={panel.customer ? `Edit ${panel.customer.name}` : "Add a customer"}
          initial={panel.customer ? toFormState(panel.customer) : BLANK_FORM}
          showActiveToggle={panel.customer !== null}
          showTaxFields={taxEnabled}
          onCancel={() => setPanel({ open: false })}
          onSave={(form) => (panel.customer ? handleUpdate(panel.customer.id, form) : handleCreate(form))}
        />
      )}

      {data && data.length === 0 && !panel.open && (
        <div className="empty-state">No customers yet. Add your first one to start invoicing.</div>
      )}

      {data && data.length > 0 && (
        <table>
          <thead>
            <tr>
              <th>Name</th>
              <th>Contact</th>
              <th>Terms</th>
              <th className="num">Balance</th>
              <th>Oldest due</th>
            </tr>
          </thead>
          <tbody>
            {data.map((c) => (
              <tr
                key={c.id}
                style={{ cursor: "pointer", opacity: c.active ? 1 : 0.6 }}
                onClick={() => setPanel({ open: true, customer: c })}
              >
                <td>
                  {c.name}
                  {!c.active && " (inactive)"}
                  {/* Leading space inside the text (not just CSS margin) so
                      copied/spoken text reads "Name (tax-exempt)". */}
                  {taxEnabled && c.taxExempt && <span className="sub">{" (tax-exempt)"}</span>}
                </td>
                <td>{[c.email, c.phone].filter(Boolean).join(" · ") || "—"}</td>
                <td>Net {c.termsDays}</td>
                <td className="num">
                  {isZeroMoney(c.balance) ? (
                    fmtMoney(c.balance)
                  ) : (
                    <button
                      type="button"
                      className="acct-link"
                      onClick={(ev) => {
                        ev.stopPropagation();
                        goToCustomerInvoices(c.id);
                      }}
                    >
                      {fmtMoney(c.balance)}
                    </button>
                  )}
                </td>
                <td>{c.oldestOpenDueDate ? fmtDate(c.oldestOpenDueDate) : "—"}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}
