import { afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, render, screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { NewAccountDialog } from "@/components/NewAccountDialog";

vi.mock("@/lib/erp", () => ({
  createAccount: vi.fn(),
  updateAccount: vi.fn(),
  postOpeningBalances: vi.fn(),
  listAccountBalancesAsOf: vi.fn(async () => []),
  listAccountLedgerLines: vi.fn(async () => []),
}));

vi.mock("sonner", () => ({ toast: { error: vi.fn(), success: vi.fn() } }));

const types = [
  { id: "t1", name: "Cash and cash equivalents", category: "asset", normal_balance: "debit" },
  { id: "t2", name: "Sales income", category: "income", normal_balance: "credit" },
];

const accounts = [
  {
    id: "a1",
    code: "1010",
    name: "Main Cash",
    account_type_id: "t1",
    parent_id: null,
    is_active: true,
    is_system: true,
    balance_locked: true,
    description: null,
    account_types: { category: "asset" },
  },
];

function renderDialog(onOpenChange = vi.fn()) {
  const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  render(
    <QueryClientProvider client={qc}>
      <NewAccountDialog
        open
        onOpenChange={onOpenChange}
        accounts={accounts as never}
        types={types as never}
        editing={null}
      />
    </QueryClientProvider>,
  );
  return { onOpenChange };
}

afterEach(cleanup);

describe("New account window accessibility and validation", () => {
  it("renders as a labelled modal dialog and autofocuses the name field", async () => {
    renderDialog();
    const dialog = screen.getByRole("dialog");
    expect(within(dialog).getByText("New account")).toBeTruthy();
    await waitFor(() => expect(document.activeElement).toBe(screen.getByLabelText(/Account name/i)));
  });

  it("keeps focus trapped inside the dialog while tabbing", async () => {
    const user = userEvent.setup();
    renderDialog();
    const dialog = screen.getByRole("dialog");
    for (let i = 0; i < 12; i += 1) {
      await user.tab();
      expect(dialog.contains(document.activeElement)).toBe(true);
    }
  });

  it("closes on Escape", async () => {
    const user = userEvent.setup();
    const { onOpenChange } = renderDialog();
    await user.keyboard("{Escape}");
    await waitFor(() => expect(onOpenChange).toHaveBeenCalledWith(false));
  });

  it("shows per-field inline errors on an invalid submit and does not close", async () => {
    const user = userEvent.setup();
    const { onOpenChange } = renderDialog();
    await user.click(screen.getByRole("button", { name: /^Save$/ }));

    const alerts = await screen.findAllByRole("alert");
    const messages = alerts.map((a) => a.textContent ?? "").join(" ");
    expect(messages).toMatch(/Account name is required/i);
    expect(messages).toMatch(/account type/i);
    expect(screen.getByLabelText(/Account name/i).getAttribute("aria-invalid")).toBe("true");
    expect(onOpenChange).not.toHaveBeenCalledWith(false);
  });

  it("rejects a duplicate account number once an account type is chosen", async () => {
    const user = userEvent.setup();
    renderDialog();
    await user.type(screen.getByLabelText(/Account name/i), "Petty Cash");
    await user.type(screen.getByLabelText(/Account number/i), "1010");

    // Account type (category) then detail type — both are Radix selects.
    await user.click(screen.getByRole("combobox", { name: /Account type/i }));
    await user.click(await screen.findByRole("option", { name: /asset/i }));
    await user.click(screen.getByRole("combobox", { name: /Detail type/i }));
    await user.click(await screen.findByRole("option", { name: /Cash and cash equivalents/i }));

    await user.click(screen.getByRole("button", { name: /^Save$/ }));

    const alerts = await screen.findAllByRole("alert");
    expect(alerts.map((a) => a.textContent ?? "").join(" ")).toMatch(/cannot be reused|already|in use/i);
  });
});
