import type { Domain } from "@domainops/shared-types";
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 cloudflareApi from "@/lib/api/cloudflare";
import { ApiError } from "@/lib/api/client";
import { CloudflarePanel } from "./CloudflarePanel";

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

function makeDomain(overrides: Partial<Domain> = {}): Domain {
  return {
    id: 1,
    fqdn: "example.com",
    tld: ".com",
    brand_id: 1,
    region_id: null,
    pic_user_id: null,
    registrar_label: null,
    purchase_price: null,
    purchased_at: null,
    expires_at: null,
    auto_renew: true,
    status: "provisioning",
    robots_state: "index",
    robots_applied_at: null,
    gsc_state: "not_started",
    gsc_verification_token: null,
    gsc_verified_at: null,
    cf_account_id: null,
    cf_zone_id: null,
    proxied: false,
    server_account_id: 1,
    notes: null,
    created_by: null,
    created_at: "2026-01-01T00:00:00Z",
    updated_at: "2026-01-01T00:00:00Z",
    brand: { id: 1, name: "Acme Brand" },
    ...overrides,
  } as Domain;
}

function renderPanel(domain: Domain, hasServer = true) {
  const onUpdated = vi.fn();
  render(
    <ConfigProvider>
      <AntdApp>
        <CloudflarePanel domain={domain} hasServer={hasServer} onUpdated={onUpdated} />
      </AntdApp>
    </ConfigProvider>,
  );
  return { onUpdated };
}

describe("CloudflarePanel", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("shows 'not provisioned' and a Provision button for a fresh domain", () => {
    renderPanel(makeDomain());

    expect(screen.getByText("not provisioned")).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Provision" })).toBeEnabled();
  });

  it("disables provisioning when the domain has no server assigned", () => {
    renderPanel(makeDomain(), false);

    expect(screen.getByRole("button", { name: "Provision" })).toBeDisabled();
    expect(screen.getByText(/Assign this domain to a server/)).toBeInTheDocument();
  });

  it("runs provisioning after confirming and shows the resulting zone and steps", async () => {
    const provisionedDomain = makeDomain({ cf_zone_id: "zone-123" });
    vi.mocked(cloudflareApi.provisionCloudflare).mockResolvedValue({
      steps: [
        { step: "create_zone", status: "done", message: "Zone created." },
        { step: "switch_nameservers", status: "done", message: "Nameservers switched." },
      ],
      domain: provisionedDomain,
    });
    const user = userEvent.setup();
    const { onUpdated } = renderPanel(makeDomain());

    await user.click(screen.getByRole("button", { name: "Provision" }));
    await user.click(await screen.findByRole("button", { name: "OK" }));

    await waitFor(() => expect(cloudflareApi.provisionCloudflare).toHaveBeenCalledWith(1));
    expect(await screen.findByText("create_zone")).toBeInTheDocument();
    expect(screen.getByText("switch_nameservers")).toBeInTheDocument();
    expect(onUpdated).toHaveBeenCalledWith(provisionedDomain);
  });

  it("shows partial progress and the error message when provisioning fails partway through", async () => {
    vi.mocked(cloudflareApi.provisionCloudflare).mockRejectedValue(
      new ApiError("Cloudflare provisioning failed partway through.", 422, {
        steps: [{ step: "create_zone", status: "failed", message: "Invalid request headers" }],
      } as unknown as Record<string, string[]>),
    );
    const user = userEvent.setup();
    const { onUpdated } = renderPanel(makeDomain());

    await user.click(screen.getByRole("button", { name: "Provision" }));
    await user.click(await screen.findByRole("button", { name: "OK" }));

    expect(await screen.findByText("Invalid request headers")).toBeInTheDocument();
    expect(screen.getByText("create_zone")).toBeInTheDocument();
    expect(onUpdated).not.toHaveBeenCalled();
  });

  it("toggles proxying for an already-provisioned domain", async () => {
    const updated = makeDomain({ cf_zone_id: "zone-123", proxied: true });
    vi.mocked(cloudflareApi.toggleCloudflareProxy).mockResolvedValue(updated);
    const user = userEvent.setup();
    const { onUpdated } = renderPanel(makeDomain({ cf_zone_id: "zone-123" }));

    await user.click(screen.getByRole("switch", { name: "Proxying" }));

    await waitFor(() => expect(cloudflareApi.toggleCloudflareProxy).toHaveBeenCalledWith(1, true));
    expect(onUpdated).toHaveBeenCalledWith(updated);
  });
});
