// Shared "New account" modal — Task 2 of
// docs/superpowers/plans/2026-08-02-chart-management.md (spec Part 2/3). One
// component backs every add-account affordance in the app:
//   - the Chart of Accounts screen's "New account" button (no preset)
//   - "+ New category" inside the Record flow's category pickers
//     (money-in -> income preset, money-out -> expense preset)
//   - "+ New category" inside invoice line pickers (income preset) and bill
//     line pickers (expense preset)
// so a category added mid-invoice is exactly the account the chart screen
// would have created (POST /api/c/accounts is the one write path).
//
// The Code field is left blank on purpose: the API auto-suggests the next
// free number in the subtype's conventional range when code is omitted
// (server/src/accounts.ts SUBTYPE_CODE_RANGES), so the placeholder just says
// so in English. Typing a code overrides the suggestion. Every server
// refusal (duplicate name, duplicate code, exhausted range, bad pairing)
// already arrives in English and is shown verbatim.

import { useRef, useState } from "react";
import { apiPost, ApiError } from "../lib/api";
import {
  DEFAULT_SUBTYPE,
  LEDGER_TYPES,
  SUBTYPES_BY_TYPE,
  SUBTYPE_LABELS,
  TYPE_LABELS_SINGULAR,
  type ApiAccount,
  type LedgerType,
} from "./subtypes";

export function NewAccountModal({
  companyId,
  presetType,
  lockType = false,
  typeNote,
  title = "New account",
  onClose,
  onCreated,
}: {
  companyId: string;
  /** Context preselect (spec Part 3): income for invoice-line/money-in
   * contexts, expense for bill-line/money-out contexts. */
  presetType?: LedgerType;
  /** Picker contexts LOCK the type to presetType (review finding 1): a
   * picker only renders the types its groups accept, so a freely-retyped
   * account would be "selected" invisibly and fail the save with a
   * misdescribing error. The chart screen's standalone modal keeps the free
   * choice (lockType false). Subtype stays fully choosable within the type. */
  lockType?: boolean;
  /** One-line English note shown with a locked Type control, explaining the
   * lock ("Invoice lines use income categories."). */
  typeNote?: string;
  title?: string;
  onClose: () => void;
  onCreated: (account: ApiAccount) => void;
}) {
  const [name, setName] = useState("");
  const [type, setType] = useState<LedgerType>(presetType ?? "expense");
  const [subtype, setSubtype] = useState(DEFAULT_SUBTYPE[presetType ?? "expense"]);
  const [code, setCode] = useState("");
  const [saving, setSaving] = useState(false);
  // Synchronous twin of `saving` (review finding 2): keydown auto-repeat can
  // deliver the next Enter before React commits the state flip, so the guard
  // reads this ref — set in the same tick — never the possibly-stale state.
  const savingRef = useRef(false);
  const [error, setError] = useState<string | null>(null);
  const [touched, setTouched] = useState(false);

  function handleTypeChange(next: LedgerType) {
    setType(next);
    // The old subtype can't fit the new type (the matrix is disjoint), so
    // always snap to the new type's generic default.
    setSubtype(DEFAULT_SUBTYPE[next]);
  }

  const nameError = touched && name.trim() === "" ? "An account needs a name." : null;

  async function handleCreate() {
    // In-flight guard (review finding 2): the Enter key auto-repeats, and
    // without this a held/mashed Enter fires one POST per keydown.
    if (savingRef.current) return;
    setTouched(true);
    if (name.trim() === "") return;
    savingRef.current = true;
    setSaving(true);
    setError(null);
    try {
      const created = await apiPost<ApiAccount>(
        "/api/c/accounts",
        {
          name: name.trim(),
          type,
          subtype,
          ...(code.trim() !== "" ? { code: code.trim() } : {}),
        },
        companyId,
      );
      onCreated(created);
    } catch (err) {
      setError(err instanceof ApiError ? err.message : "Couldn't add that account. Try again.");
    } finally {
      savingRef.current = false;
      setSaving(false);
    }
  }

  return (
    <div
      className="modal-overlay"
      onMouseDown={(ev) => { if (ev.target === ev.currentTarget) onClose(); }}
      onKeyDown={(ev) => { if (ev.key === "Escape") onClose(); }}
    >
      <div className="modal-card" role="dialog" aria-modal="true" aria-label={title}>
        <h2>{title}</h2>

        <div className="field">
          <label htmlFor="new-acct-name">Name</label>
          <input
            id="new-acct-name"
            className="text-input"
            type="text"
            autoFocus
            value={name}
            onChange={(ev) => setName(ev.target.value)}
            onKeyDown={(ev) => {
              if (ev.key === "Enter") void handleCreate();
            }}
          />
          {nameError && <p className="field-error">{nameError}</p>}
        </div>

        <div className="field-row">
          <div className="field">
            <label htmlFor="new-acct-type">Type</label>
            <select
              id="new-acct-type"
              className="select-input"
              value={type}
              disabled={lockType}
              onChange={(ev) => handleTypeChange(ev.target.value as LedgerType)}
            >
              {LEDGER_TYPES.map((t) => (
                <option key={t} value={t}>
                  {TYPE_LABELS_SINGULAR[t]}
                </option>
              ))}
            </select>
          </div>
          <div className="field">
            <label htmlFor="new-acct-subtype">Grouping</label>
            <select
              id="new-acct-subtype"
              className="select-input"
              value={subtype}
              onChange={(ev) => setSubtype(ev.target.value as (typeof SUBTYPES_BY_TYPE)[LedgerType][number])}
            >
              {SUBTYPES_BY_TYPE[type].map((s) => (
                <option key={s} value={s}>
                  {SUBTYPE_LABELS[s]}
                </option>
              ))}
            </select>
          </div>
        </div>

        {lockType && typeNote && (
          <p className="sub" style={{ marginTop: -6, marginBottom: 12 }}>{typeNote}</p>
        )}

        <div className="field">
          <label htmlFor="new-acct-code">Code (optional)</label>
          <input
            id="new-acct-code"
            className="text-input"
            type="text"
            placeholder="We'll pick the next free number"
            value={code}
            onChange={(ev) => setCode(ev.target.value)}
          />
        </div>

        {error && <p className="field-error">{error}</p>}

        <div className="wizard-actions">
          <button type="button" className="btn" disabled={saving} onClick={() => void handleCreate()}>
            {saving ? "Adding…" : "Add account"}
          </button>
          <button type="button" className="acct-link" disabled={saving} onClick={onClose}>
            Cancel
          </button>
        </div>
      </div>
    </div>
  );
}
