// Phase 10 end-to-end coverage for the in-game shell. Every spec
// boots an authenticated session through `/__debug/store` (the
// in-game shell makes a handful of gateway calls — for the lobby
// record, the report, and the order read-back; we don't mock them
// here, the shell tolerates ECONNREFUSED), navigates into
// `/games/<game-id>/map`, and exercises one slice of the chrome:
// header navigation, sidebar tab preservation, mobile bottom-tabs,
// and the breakpoint switches at 768 / 1024 px.

import { expect, test, type Page } from "@playwright/test";

// The `window.__galaxyDebug` surface is owned by
// `src/routes/__debug/store/+page.svelte` and typed by
// `tests/e2e/storage-keypair-persistence.spec.ts`. This spec only
// needs the auth-bootstrap subset (`clearSession`,
// `setDeviceSessionId`); the merged global declaration covers both.

const SESSION_ID = "phase-10-shell-session";
// GAME_ID has to be a real UUID — Phase 14's auto-sync calls
// `uuidToHiLo` on it for the FBS request envelope, and an
// arbitrary string would throw on every layout boot.
const GAME_ID = "10101010-1010-1010-1010-101010101010";

async function bootShell(page: Page): Promise<void> {
	await page.goto("/__debug/store");
	await expect(page.getByTestId("debug-store-ready")).toBeVisible();
	await page.waitForFunction(() => window.__galaxyDebug?.ready === true);
	await page.evaluate(() => window.__galaxyDebug!.clearSession());
	await page.evaluate(
		(id) => window.__galaxyDebug!.setDeviceSessionId(id),
		SESSION_ID,
	);
	await page.goto(`/games/${GAME_ID}/map`);
	await expect(page.getByTestId("game-shell")).toBeVisible();
	await expect(page.getByTestId("active-view-map")).toBeVisible();
}

test("shell mounts with header / sidebar / active-view chrome", async ({
	page,
}) => {
	await bootShell(page);
	await expect(page.getByTestId("game-shell-header")).toBeVisible();
	await expect(page.getByTestId("game-shell-headline")).toContainText(
		"turn",
	);
	await expect(page.getByTestId("view-menu-trigger")).toBeVisible();
	await expect(page.getByTestId("account-menu-trigger")).toBeVisible();
});

test("header view-menu navigates to every active view", async ({ page }) => {
	await bootShell(page);

	const destinations: Array<[string, string, string]> = [
		["view-menu-item-report", "active-view-report", "/report"],
		["view-menu-item-mail", "active-view-mail", "/mail"],
		["view-menu-item-battle", "active-view-battle", "/battle"],
		[
			"view-menu-item-designer-ship-class",
			"active-view-designer-ship-class",
			"/designer/ship-class",
		],
		[
			"view-menu-item-designer-science",
			"active-view-designer-science",
			"/designer/science",
		],
		["view-menu-item-map", "active-view-map", "/map"],
	];

	for (const [trigger, viewTestId, urlSuffix] of destinations) {
		await page.getByTestId("view-menu-trigger").click();
		await page.getByTestId(trigger).click();
		await expect(page.getByTestId(viewTestId)).toBeVisible();
		await expect(page).toHaveURL(new RegExp(`/games/${GAME_ID}${urlSuffix}$`));
	}
});

test("header view-menu Tables sub-list navigates to every entity", async ({
	page,
}) => {
	await bootShell(page);
	const entities = [
		"planets",
		"ship-classes",
		"ship-groups",
		"fleets",
		"sciences",
		"races",
	];
	for (const entity of entities) {
		await page.getByTestId("view-menu-trigger").click();
		await page
			.getByTestId("view-menu-tables")
			.locator("summary")
			.click();
		await page.getByTestId(`view-menu-item-table-${entity}`).click();
		const view = page.getByTestId("active-view-table");
		await expect(view).toBeVisible();
		await expect(view).toHaveAttribute("data-entity", entity);
		await expect(page).toHaveURL(
			new RegExp(`/games/${GAME_ID}/table/${entity}$`),
		);
	}
});

test("sidebar tab choice survives navigation between active views", async ({
	page,
	browserName,
}, testInfo) => {
	test.skip(
		testInfo.project.name.startsWith("chromium-mobile") ||
			testInfo.project.name === "webkit-desktop"
			? false
			: false,
		"sidebar test runs on every project",
	);
	await bootShell(page);
	// Skip on viewports below 1024 — sidebar is hidden by CSS there.
	const viewport = page.viewportSize();
	if (viewport === null || viewport.width < 1024) {
		test.skip();
		return;
	}
	void browserName;

	await page.getByTestId("sidebar-tab-calculator").click();
	await expect(page.getByTestId("sidebar-tool-calculator")).toBeVisible();

	await page.getByTestId("view-menu-trigger").click();
	await page.getByTestId("view-menu-item-report").click();
	await expect(page.getByTestId("active-view-report")).toBeVisible();

	// Sidebar still rendered; the calculator tool remains selected.
	await expect(page.getByTestId("sidebar-tool-calculator")).toBeVisible();
	await expect(page.getByTestId("sidebar")).toHaveAttribute(
		"data-active-tab",
		"calculator",
	);

	await page.getByTestId("view-menu-trigger").click();
	await page.getByTestId("view-menu-item-map").click();
	await expect(page.getByTestId("active-view-map")).toBeVisible();
	await expect(page.getByTestId("sidebar")).toHaveAttribute(
		"data-active-tab",
		"calculator",
	);
});

test("mobile bottom-tabs show on small viewports and toggle the tool overlay", async ({
	page,
}, testInfo) => {
	if (!testInfo.project.name.startsWith("chromium-mobile")) {
		test.skip();
		return;
	}
	await bootShell(page);

	await expect(page.getByTestId("bottom-tabs")).toBeVisible();
	await expect(page.getByTestId("sidebar")).not.toBeVisible();

	await page.getByTestId("bottom-tab-calc").click();
	await expect(page.getByTestId("sidebar-tool-calculator")).toBeVisible();

	await page.getByTestId("bottom-tab-order").click();
	await expect(page.getByTestId("sidebar-tool-order")).toBeVisible();

	await page.getByTestId("bottom-tab-map").click();
	await expect(page.getByTestId("active-view-map")).toBeVisible();
});

test("mobile More drawer navigates to every destination", async ({
	page,
}, testInfo) => {
	if (!testInfo.project.name.startsWith("chromium-mobile")) {
		test.skip();
		return;
	}
	await bootShell(page);

	await page.getByTestId("bottom-tab-more").click();
	await expect(page.getByTestId("bottom-tabs-more-drawer")).toBeVisible();
	await page.getByTestId("bottom-tabs-more-mail").click();
	await expect(page.getByTestId("active-view-mail")).toBeVisible();

	await page.getByTestId("bottom-tab-more").click();
	await page.getByTestId("bottom-tabs-more-report").click();
	await expect(page.getByTestId("active-view-report")).toBeVisible();
});

test("breakpoint switches between desktop / tablet / mobile", async ({
	page,
}, testInfo) => {
	// Use a single chromium-desktop run to drive all three viewports in
	// the same browser. Other projects skip — the viewport diff is the
	// goal here, not browser-specific behaviour.
	if (testInfo.project.name !== "chromium-desktop") {
		test.skip();
		return;
	}
	await bootShell(page);

	// Desktop ≥ 1024: sidebar visible, bottom-tabs hidden, sidebar
	// toggle hidden.
	await page.setViewportSize({ width: 1280, height: 800 });
	await expect(page.getByTestId("sidebar")).toBeVisible();
	await expect(page.getByTestId("bottom-tabs")).not.toBeVisible();
	await expect(page.getByTestId("sidebar-toggle")).not.toBeVisible();

	// Tablet 768–1024: sidebar hidden by default, sidebar toggle
	// visible, bottom-tabs hidden. Click the toggle and the sidebar
	// becomes visible again.
	await page.setViewportSize({ width: 900, height: 800 });
	await expect(page.getByTestId("sidebar")).not.toBeVisible();
	await expect(page.getByTestId("sidebar-toggle")).toBeVisible();
	await expect(page.getByTestId("bottom-tabs")).not.toBeVisible();
	await page.getByTestId("sidebar-toggle").click();
	await expect(page.getByTestId("sidebar")).toBeVisible();

	// Mobile < 768: sidebar hidden entirely, bottom-tabs visible,
	// sidebar toggle hidden again.
	await page.setViewportSize({ width: 390, height: 800 });
	await expect(page.getByTestId("bottom-tabs")).toBeVisible();
	await expect(page.getByTestId("sidebar")).not.toBeVisible();
	await expect(page.getByTestId("sidebar-toggle")).not.toBeVisible();
});
