import { App as AntdApp, ConfigProvider } from "antd";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi, beforeEach } from "vitest";
import * as domainsApi from "@/lib/api/domains";
import * as serversApi from "@/lib/api/servers";
import * as acquireApi from "@/lib/api/acquire";
import * as cloudflareApi from "@/lib/api/cloudflare";
import { ApiError } from "@/lib/api/client";
import { AddDomainModal } from "./AddDomainModal";

vi.mock("@/lib/api/domains");
vi.mock("@/lib/api/servers");
vi.mock("@/lib/api/acquire");
vi.mock("@/lib/api/cloudflare");

function renderModal(props: Partial<React.ComponentProps<typeof AddDomainModal>> = {}) {
  const onClose = vi.fn();
  const onCreated = vi.fn();
  render(
    <ConfigProvider>
      <AntdApp>
        <AddDomainModal open onClose={onClose} onCreated={onCreated} {...props} />
      </AntdApp>
    </ConfigProvider>,
  );
  return { onClose, onCreated };
}

describe("AddDomainModal", () => {
  beforeEach(() => {
    vi.mocked(domainsApi.listBrands).mockResolvedValue([]);
    vi.mocked(domainsApi.listRegions).mockResolvedValue([]);
    vi.mocked(serversApi.listServerAccounts).mockResolvedValue([]);
    vi.mocked(serversApi.listServerGroups).mockResolvedValue([]);
    vi.mocked(cloudflareApi.listCloudflareAccounts).mockResolvedValue([]);
    vi.mocked(acquireApi.listRegistrars).mockResolvedValue({ accounts: [], egress_ip: null, supported_providers: [] });
  });

  it("renders the form fields", async () => {
    renderModal();

    expect(await screen.findByLabelText("Domain name")).toBeInTheDocument();
    expect(screen.getByLabelText("Project")).toBeInTheDocument();
    expect(screen.getByLabelText("Region")).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Save only - set up later" })).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Proceed to setup" })).toBeInTheDocument();
    expect(screen.queryByLabelText("Expires")).not.toBeInTheDocument();
  });

  it("asks for the server, purpose and registrar before proceeding to setup", async () => {
    const user = userEvent.setup();
    renderModal();
    await screen.findByLabelText("Domain name");

    await user.type(screen.getByLabelText("Domain name"), "example.com");
    await user.click(screen.getByRole("button", { name: "Proceed to setup" }));

    expect(await screen.findByText(/To set it up now, pick/)).toBeInTheDocument();
    expect(domainsApi.createDomain).not.toHaveBeenCalled();
  });

  it("requires a domain name before submitting", async () => {
    const user = userEvent.setup();
    renderModal();
    await screen.findByLabelText("Domain name");

    await user.click(screen.getByRole("button", { name: "Save only - set up later" }));

    expect(await screen.findByText("Enter the domain name")).toBeInTheDocument();
    expect(domainsApi.createDomain).not.toHaveBeenCalled();
  });

  it("submits the fqdn and calls onCreated on success", async () => {
    vi.mocked(domainsApi.createDomain).mockResolvedValue({ id: 1, fqdn: "example.com" } as never);
    const user = userEvent.setup();
    const { onCreated } = renderModal();
    await screen.findByLabelText("Domain name");

    await user.type(screen.getByLabelText("Domain name"), "example.com");
    await user.click(screen.getByRole("button", { name: "Save only - set up later" }));

    await waitFor(() =>
      expect(domainsApi.createDomain).toHaveBeenCalledWith(expect.objectContaining({ fqdn: "example.com" })),
    );
    await waitFor(() => expect(onCreated).toHaveBeenCalled());
  });

  it("shows the server's error message and does not call onCreated when creation fails", async () => {
    vi.mocked(domainsApi.createDomain).mockRejectedValue(
      new ApiError("This domain is already in inventory.", 422, { fqdn: ["This domain is already in inventory."] }),
    );
    const user = userEvent.setup();
    const { onCreated } = renderModal();
    await screen.findByLabelText("Domain name");

    await user.type(screen.getByLabelText("Domain name"), "taken.com");
    await user.click(screen.getByRole("button", { name: "Save only - set up later" }));

    await waitFor(() => expect(domainsApi.createDomain).toHaveBeenCalled());
    expect(await screen.findByText(/taken.com: This domain is already in inventory./)).toBeInTheDocument();
    expect(onCreated).not.toHaveBeenCalled();
  });

  it("adds every domain typed on its own line", async () => {
    vi.mocked(domainsApi.createDomain).mockResolvedValue({ id: 1, fqdn: "x" } as never);
    const user = userEvent.setup();
    const { onCreated } = renderModal();
    await screen.findByLabelText("Domain name");

    await user.type(screen.getByLabelText("Domain name"), "one.com{enter}two.net, https://three.org/path");
    await user.click(screen.getByRole("button", { name: "Save only - set up later" }));

    await waitFor(() => expect(domainsApi.createDomain).toHaveBeenCalledTimes(3));
    expect(domainsApi.createDomain).toHaveBeenCalledWith(expect.objectContaining({ fqdn: "one.com" }));
    expect(domainsApi.createDomain).toHaveBeenCalledWith(expect.objectContaining({ fqdn: "two.net" }));
    expect(domainsApi.createDomain).toHaveBeenCalledWith(expect.objectContaining({ fqdn: "three.org" }));
    await waitFor(() => expect(onCreated).toHaveBeenCalled());
  });
});
