// Phase 20 end-to-end coverage for the ship-group Send action.
// Loads a synthetic report with a local group of three Frontier
// ships in orbit over Earth and a reachable destination planet
// (Mars), opens the inspector by clicking the rendered group,
// drives the Send form (asking for 2 ships out of 3), picks Mars
// through the map-pick service, and asserts the resulting order
// draft has both an implicit `breakShipGroup` and the targeted
// `sendShipGroup` whose `groupId` references the freshly minted
// sub-group ID. The synthetic flow uses a non-UUID game id, so
// the auto-sync pipeline skips the network — the assertion
// targets the in-memory draft via the order-tab UI.

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

const SESSION_ID = "phase-20-send-session";

// `Window.__galaxyDebug` is declared as a global in
// `tests/e2e/storage-keypair-persistence.spec.ts`; reuse that
// declaration so the two specs do not collide on the symbol type.

const SYNTHETIC_FIXTURE = {
	turn: 1,
	mapWidth: 200,
	mapHeight: 200,
	mapPlanets: 2,
	race: "Earthlings",
	player: [
		{
			name: "Earthlings",
			drive: 5,
			weapons: 0,
			shields: 0,
			cargo: 1,
			population: 1000,
			industry: 1000,
			planets: 1,
			relation: "-",
			votes: 0,
			extinct: false,
		},
		{
			name: "Aliens",
			drive: 4,
			weapons: 2,
			shields: 1,
			cargo: 1,
			population: 800,
			industry: 800,
			planets: 1,
			relation: "-",
			votes: 0,
			extinct: false,
		},
	],
	localPlanet: [
		{
			number: 1,
			name: "Earth",
			x: 100,
			y: 100,
			size: 1000,
			population: 1000,
			industry: 1000,
			resources: 10,
			production: "Capital",
			capital: 0,
			material: 0,
			colonists: 100,
			freeIndustry: 1000,
		},
	],
	otherPlanet: [
		{
			number: 2,
			name: "Mars",
			x: 110,
			y: 100,
			size: 800,
			population: 800,
			industry: 800,
			resources: 8,
			production: "Capital",
			capital: 0,
			material: 0,
			colonists: 80,
			freeIndustry: 800,
			owner: "Aliens",
		},
	],
	uninhabitedPlanet: [],
	unidentifiedPlanet: [],
	localShipClass: [
		{
			name: "Frontier",
			drive: 5,
			armament: 0,
			weapons: 0,
			shields: 0,
			cargo: 1,
			mass: 12,
		},
	],
	localGroup: [
		{
			id: "11111111-2222-3333-4444-555555555555",
			number: 3,
			class: "Frontier",
			tech: { drive: 5, weapons: 0, shields: 0, cargo: 1 },
			cargo: "-",
			load: 0,
			destination: 1,
			speed: 25,
			mass: 12,
			state: "In_Orbit",
		},
	],
	otherGroup: [],
	incomingGroup: [],
	unidentifiedGroup: [],
	localFleet: [],
};

async function bootSession(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(async () => {
		const debug = window.__galaxyDebug!;
		await debug.loadSession();
		await debug.setDeviceSessionId("phase-20-send-session");
	});
	void SESSION_ID;
}

async function loadSyntheticGame(page: Page): Promise<void> {
	await page.goto("/lobby");
	await expect(page.getByTestId("lobby-synthetic-section")).toBeVisible();
	const file = page.getByTestId("lobby-synthetic-file");
	await file.setInputFiles({
		name: "phase20.json",
		mimeType: "application/json",
		buffer: Buffer.from(JSON.stringify(SYNTHETIC_FIXTURE)),
	});
	await page.waitForURL(/\/games\/synthetic-[^/]+\/map$/, {
		timeout: 10_000,
	});
	await expect(page.getByTestId("active-view-map")).toHaveAttribute(
		"data-status",
		"ready",
	);
}

// projectWorldToScreen returns the pixel coordinates of a world-space
// point (x, y) relative to the document, using the renderer's
// debug-surface camera snapshot. Waits for the renderer to register
// its debug providers (the in-game shell calls
// `installRendererDebugSurface` on mount, then the providers attach
// when `mountRenderer` resolves) so the spec is robust against the
// async Pixi boot.
async function projectWorldToScreen(
	page: Page,
	x: number,
	y: number,
): Promise<{ x: number; y: number }> {
	await page.waitForFunction(() => {
		const dbg = window.__galaxyDebug as unknown as
			| { getMapCamera(): unknown }
			| undefined;
		if (dbg === undefined) return false;
		return dbg.getMapCamera() !== null;
	});
	return page.evaluate(({ wx, wy }) => {
		const debug = window.__galaxyDebug as unknown as {
			getMapCamera(): {
				camera: { centerX: number; centerY: number; scale: number };
				viewport: { widthPx: number; heightPx: number };
				canvasOrigin: { x: number; y: number };
			} | null;
		};
		const cam = debug.getMapCamera();
		if (cam === null) throw new Error("camera unavailable");
		const sx = cam.canvasOrigin.x + cam.viewport.widthPx / 2 +
			(wx - cam.camera.centerX) * cam.camera.scale;
		const sy = cam.canvasOrigin.y + cam.viewport.heightPx / 2 +
			(wy - cam.camera.centerY) * cam.camera.scale;
		return { x: sx, y: sy };
	}, { wx: x, wy: y });
}

test("send 2 of 3 ships emits implicit Break + Send into the order draft", async ({
	page,
}, testInfo) => {
	test.skip(
		testInfo.project.name.startsWith("chromium-mobile"),
		"phase 20 spec covers desktop layout; mobile inherits the same store",
	);

	await bootSession(page);
	await loadSyntheticGame(page);

	// On-planet ship groups are *not* rendered as map primitives (the
	// renderer hides them to avoid crowding); the player navigates to
	// them through the planet inspector's stationed-ship row, which
	// pivots the SelectionStore to the ship-group variant.
	const earthScreen = await projectWorldToScreen(page, 100, 100);
	await page.mouse.click(earthScreen.x, earthScreen.y);

	const sidebar = page.getByTestId("sidebar-tool-inspector");
	await expect(sidebar.getByTestId("inspector-planet-name")).toHaveText("Earth");
	await sidebar
		.getByTestId("inspector-planet-ship-groups-select")
		.first()
		.click();
	await expect(
		sidebar.getByTestId("inspector-ship-group-class"),
	).toHaveText("Frontier");

	// Click Send: the inspector enters map-pick mode immediately; the
	// form (ship count + confirm) only mounts after the destination
	// is chosen.
	await sidebar.getByTestId("inspector-ship-group-action-send").click();
	await expect(
		sidebar.getByTestId("inspector-ship-group-form-send-pick-prompt"),
	).toBeVisible();

	const marsScreen = await projectWorldToScreen(page, 110, 100);
	await page.mouse.click(marsScreen.x, marsScreen.y);

	const sendShips = sidebar.getByTestId("inspector-ship-group-form-send-ships");
	await sendShips.fill("2");
	await expect(
		sidebar.getByTestId("inspector-ship-group-form-send-destination"),
	).toContainText("Mars");

	// Confirm.
	await sidebar.getByTestId("inspector-ship-group-form-send-confirm").click();

	// Verify the order tab carries both commands in submission order.
	await page.getByTestId("sidebar-tab-order").click();
	const orderTool = page.getByTestId("sidebar-tool-order");
	await expect(orderTool.getByTestId("order-command-label-0")).toContainText(
		"split group",
	);
	await expect(orderTool.getByTestId("order-command-label-1")).toContainText(
		"send group",
	);
});
