import { act, fireEvent, render, screen } from "@testing-library/react";
import type { LiveQueryEvent } from "@/lib/types";
import { FakeEventSource } from "./fakeEventSource";
import LiveLogPage from "./LiveLogPage";

function frame(ts: number, domain: string, overrides: Partial<LiveQueryEvent> = {}): { data: string } {
	const payload: LiveQueryEvent = {
		ts,
		domain,
		client_ip: "192.0.2.10",
		qtype: 1,
		blocked: false,
		block_reason: "",
		response_time_us: 500,
		cache_hit: true,
		upstream: "",
		...overrides,
	};
	return { data: JSON.stringify(payload) };
}

function renderPage() {
	const sources: FakeEventSource[] = [];
	const createEventSource = (url: string) => {
		const es = new FakeEventSource(url);
		sources.push(es);
		return es;
	};
	render(<LiveLogPage createEventSource={createEventSource} />);
	return sources;
}

test("streams rows, flags blocked ones, and freezes the display", () => {
	const sources = renderPage();
	expect(screen.getByText("Connecting…")).toBeTruthy();

	act(() => sources[0]!.emit("open"));
	expect(screen.getByRole("status", { name: "Live" })).toBeTruthy();
	expect(screen.getByText("Waiting for queries…")).toBeTruthy();

	act(() => {
		sources[0]!.emit("query", frame(1000, "ok.example"));
		sources[0]!.emit(
			"query",
			frame(1001, "ads.example", { blocked: true, block_reason: "blocklist:stevenblack", qtype: 28 }),
		);
	});

	expect(screen.getByText("ok.example")).toBeTruthy();
	expect(screen.getByText("Blocked")).toBeTruthy();
	expect(screen.getByText("blocklist:stevenblack")).toBeTruthy();
	expect(screen.getByText("AAAA")).toBeTruthy();
	// StyleX compiles to opaque class names, so the check is structural: a blocked
	// row carries every class a plain row does, plus the ones the flag adds.
	const blockedRow = screen.getByText("ads.example").closest("tr");
	const plainRow = screen.getByText("ok.example").closest("tr");
	const blockedClasses = new Set(blockedRow?.className.split(" "));
	const plainClasses = plainRow?.className.split(" ") ?? [];
	expect(plainClasses.every((name) => blockedClasses.has(name))).toBe(true);
	expect(blockedClasses.size).toBeGreaterThan(plainClasses.length);

	const freeze = screen.getByRole("button", { name: "Freeze" });
	fireEvent.click(freeze);
	expect(freeze.getAttribute("aria-pressed")).toBe("true");

	act(() => sources[0]!.emit("query", frame(1002, "later.example")));
	expect(screen.queryByText("later.example")).toBeNull();
	expect(screen.getByText(/3 in buffer/)).toBeTruthy();

	fireEvent.click(screen.getByRole("button", { name: "Resume" }));
	expect(screen.getByText("later.example")).toBeTruthy();
});

test("repeated connection failures show the viewer-cap state with a retry button", () => {
	const sources = renderPage();
	act(() => {
		sources[0]!.emit("error");
		sources[0]!.emit("error");
		sources[0]!.emit("error");
	});
	expect(screen.getByRole("alert").textContent).toContain("too many live viewers");

	fireEvent.click(screen.getByRole("button", { name: "Retry" }));
	expect(sources).toHaveLength(2);
	expect(screen.getByText("Connecting…")).toBeTruthy();
});
