import { fireEvent, render, screen } from "@testing-library/react";
import { QueryClientProvider } from "@tanstack/react-query";
import { RouterProvider, createMemoryHistory } from "@tanstack/react-router";
import { AuthProvider } from "@/auth/store";
import { createQueryClient } from "@/lib/queryClient";
import { createAppRouter } from "@/routes";

const GROUPS = {
	groups: [
		{ id: 1, name: "default", safe_search: false },
		{ id: 2, name: "kids", safe_search: true },
	],
};

const CLIENTS = {
	clients: [
		{
			id: 1,
			ip: "192.168.1.10",
			name: "laptop",
			group_id: 1,
			group: "default",
			hand_edited: true,
			first_seen: 1700000000,
			last_seen: 1700003600,
		},
		{
			id: 2,
			ip: "192.168.1.11",
			name: "",
			group_id: 2,
			group: "kids",
			hand_edited: false,
			first_seen: 1700000000,
			last_seen: 1700007200,
		},
	],
};

const PREFIXES = {
	client_prefixes: [{ id: 1, prefix: "192.168.1.0/24", group_id: 2, group: "kids", priority: 100 }],
};

const VERSION = { version: "0.0.0-test", git_commit: "0000000", zig_version: "0.16.0", uptime_seconds: 1 };

function stubFetch(map: Record<string, unknown>) {
	vi.stubGlobal(
		"fetch",
		vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
			const key = `${init?.method ?? "GET"} ${String(input)}`;
			const payload = map[key];
			if (payload === undefined) {
				return new Response(JSON.stringify({ error: `not stubbed: ${key}` }), { status: 404 });
			}
			return new Response(JSON.stringify(payload), {
				status: 200,
				headers: { "content-type": "application/json" },
			});
		}),
	);
}

async function renderClientsPage(map: Record<string, unknown>) {
	stubFetch(map);
	const queryClient = createQueryClient();
	const router = createAppRouter(createMemoryHistory({ initialEntries: ["/clients"] }), queryClient);
	render(
		<AuthProvider>
			<QueryClientProvider client={queryClient}>
				<RouterProvider router={router} />
			</QueryClientProvider>
		</AuthProvider>,
	);
	await screen.findByRole("heading", { name: "Clients" });
}

const BASE = {
	"GET /api/clients": CLIENTS,
	"GET /api/client-prefixes": PREFIXES,
	"GET /api/groups": GROUPS,
	"GET /api/version": VERSION,
};

afterEach(() => {
	vi.unstubAllGlobals();
});

test("renders the client table with group names and one hand-edited badge", async () => {
	await renderClientsPage(BASE);

	expect(screen.getByText("192.168.1.10")).toBeTruthy();
	expect(screen.getByText("192.168.1.11")).toBeTruthy();
	expect(screen.getByText("laptop")).toBeTruthy();
	expect(screen.getAllByText("edited")).toHaveLength(1);
	expect(screen.getAllByRole("cell", { name: "kids" })).toHaveLength(1);
});

test("shows the DNS-activity empty state when there are no clients", async () => {
	await renderClientsPage({ ...BASE, "GET /api/clients": { clients: [] } });

	expect(screen.getByText(/rows appear automatically as devices on the network make dns queries/i)).toBeTruthy();
	expect(screen.queryByRole("table")).toBeNull();
});

test("edit opens a dialog seeded with the client's name and group", async () => {
	await renderClientsPage(BASE);

	fireEvent.click(screen.getAllByRole("button", { name: "Edit" })[0]!);
	const dialog = screen.getByRole("dialog", { name: "Edit client 192.168.1.10" });
	expect(dialog).toBeTruthy();
	expect((screen.getByLabelText("Name") as HTMLInputElement).value).toBe("laptop");
	expect((screen.getByLabelText("Group") as HTMLSelectElement).value).toBe("1");
});

test("prefix editor starts clean and dirties on add", async () => {
	await renderClientsPage(BASE);

	expect((screen.getByLabelText("Prefix 1") as HTMLInputElement).value).toBe("192.168.1.0/24");
	const save = screen.getByRole("button", { name: "Save prefixes" }) as HTMLButtonElement;
	expect(save.disabled).toBe(true);

	fireEvent.click(screen.getByRole("button", { name: "Add prefix" }));
	expect(save.disabled).toBe(false);
	expect((screen.getByLabelText("Prefix 2") as HTMLInputElement).value).toBe("");
});
