// Pure-function coverage for `map/cargo-routes.ts.buildCargoRouteLines`.
// The renderer turns each `ReportRouteEntry` into one shaft plus two
// arrowhead wings; the tests assert geometry on a flat fixture, on a
// torus seam-crossing fixture, and the per-load-type style/priority
// mapping. Pixi-free — the helper is a pure projection of the report.

import { describe, expect, test } from "vitest";

import type {
	GameReport,
	ReportPlanet,
	ReportRouteEntry,
} from "../src/api/game-state";
import {
	ROUTE_LINE_ID_PREFIX,
	STYLE_ROUTE_CAP,
	STYLE_ROUTE_COL,
	STYLE_ROUTE_EMP,
	STYLE_ROUTE_MAT,
	buildCargoRouteLines,
} from "../src/map/cargo-routes";
import { EMPTY_SHIP_GROUPS } from "./helpers/empty-ship-groups";

function makePlanet(overrides: Partial<ReportPlanet>): ReportPlanet {
	return {
		number: 0,
		name: "",
		x: 0,
		y: 0,
		kind: "local",
		owner: null,
		size: null,
		resources: null,
		industryStockpile: null,
		materialsStockpile: null,
		industry: null,
		population: null,
		colonists: null,
		production: null,
		freeIndustry: null,
		...overrides,
	};
}

function makeReport(
	planets: ReportPlanet[],
	source: number,
	entries: ReportRouteEntry[],
	mapWidth = 1000,
	mapHeight = 1000,
): GameReport {
	return {
		turn: 1,
		mapWidth,
		mapHeight,
		planetCount: planets.length,
		planets,
		race: "Earthlings",
		localShipClass: [],
		routes: [{ sourcePlanetNumber: source, entries }],
		localPlayerDrive: 1,
		localPlayerWeapons: 0,
		localPlayerShields: 0,
		localPlayerCargo: 0,
		...EMPTY_SHIP_GROUPS,
	};
}

describe("buildCargoRouteLines", () => {
	test("emits one shaft + two wings per route entry", () => {
		const report = makeReport(
			[
				makePlanet({ number: 1, x: 100, y: 100 }),
				makePlanet({ number: 2, x: 300, y: 100 }),
			],
			1,
			[{ loadType: "COL", destinationPlanetNumber: 2 }],
		);
		const lines = buildCargoRouteLines(report);
		expect(lines.length).toBe(3);
		expect(lines.every((l) => l.kind === "line")).toBe(true);
	});

	test("shaft endpoints follow the no-wrap straight line", () => {
		const report = makeReport(
			[
				makePlanet({ number: 1, x: 100, y: 100 }),
				makePlanet({ number: 2, x: 300, y: 100 }),
			],
			1,
			[{ loadType: "COL", destinationPlanetNumber: 2 }],
		);
		const [shaft] = buildCargoRouteLines(report);
		expect(shaft).toBeDefined();
		if (shaft === undefined) return;
		expect(shaft.x1).toBe(100);
		expect(shaft.y1).toBe(100);
		expect(shaft.x2).toBe(300);
		expect(shaft.y2).toBe(100);
	});

	test("shaft uses the torus-shortest delta on the seam", () => {
		// Source at x=950, dest at x=50 in a world 1000 wide. The
		// shorter wrap is +100 (right past x=1000 to x=1050), not
		// −900 (left to x=50).
		const report = makeReport(
			[
				makePlanet({ number: 1, x: 950, y: 500 }),
				makePlanet({ number: 2, x: 50, y: 500 }),
			],
			1,
			[{ loadType: "MAT", destinationPlanetNumber: 2 }],
			1000,
			1000,
		);
		const [shaft] = buildCargoRouteLines(report);
		expect(shaft).toBeDefined();
		if (shaft === undefined) return;
		expect(shaft.x1).toBe(950);
		expect(shaft.x2).toBe(1050); // 950 + 100
		expect(shaft.y2).toBe(500);
	});

	test("each load type maps to the documented style and priority", () => {
		const report = makeReport(
			[
				makePlanet({ number: 1, x: 100, y: 100 }),
				makePlanet({ number: 2, x: 200, y: 100 }),
				makePlanet({ number: 3, x: 300, y: 100 }),
				makePlanet({ number: 4, x: 400, y: 100 }),
				makePlanet({ number: 5, x: 500, y: 100 }),
			],
			1,
			[
				{ loadType: "COL", destinationPlanetNumber: 2 },
				{ loadType: "CAP", destinationPlanetNumber: 3 },
				{ loadType: "MAT", destinationPlanetNumber: 4 },
				{ loadType: "EMP", destinationPlanetNumber: 5 },
			],
		);
		const lines = buildCargoRouteLines(report);
		expect(lines.length).toBe(12);
		const styleByPriority = new Map<number, typeof lines[number]["style"]>();
		for (const line of lines) {
			const existing = styleByPriority.get(line.priority);
			if (existing === undefined) styleByPriority.set(line.priority, line.style);
			else expect(existing).toBe(line.style);
		}
		expect(styleByPriority.get(8)).toBe(STYLE_ROUTE_COL);
		expect(styleByPriority.get(7)).toBe(STYLE_ROUTE_CAP);
		expect(styleByPriority.get(6)).toBe(STYLE_ROUTE_MAT);
		expect(styleByPriority.get(5)).toBe(STYLE_ROUTE_EMP);
	});

	test("line ids carry the ROUTE_LINE_ID_PREFIX high bit", () => {
		const report = makeReport(
			[
				makePlanet({ number: 1, x: 100, y: 100 }),
				makePlanet({ number: 2, x: 200, y: 100 }),
			],
			1,
			[{ loadType: "COL", destinationPlanetNumber: 2 }],
		);
		const lines = buildCargoRouteLines(report);
		for (const line of lines) {
			expect((line.id & ROUTE_LINE_ID_PREFIX) !== 0).toBe(true);
		}
		// Three distinct ids — one per segment.
		const ids = new Set(lines.map((l) => l.id));
		expect(ids.size).toBe(3);
	});

	test("skips routes whose source or destination is missing", () => {
		const report = makeReport(
			[makePlanet({ number: 1, x: 100, y: 100 })],
			1,
			[
				{ loadType: "COL", destinationPlanetNumber: 999 }, // unknown dest
			],
		);
		expect(buildCargoRouteLines(report).length).toBe(0);
	});

	test("skips zero-length routes (source == destination coords)", () => {
		const report = makeReport(
			[
				makePlanet({ number: 1, x: 100, y: 100 }),
				makePlanet({ number: 2, x: 100, y: 100 }),
			],
			1,
			[{ loadType: "COL", destinationPlanetNumber: 2 }],
		);
		expect(buildCargoRouteLines(report).length).toBe(0);
	});

	test("returns an empty array when no routes are configured", () => {
		const report: GameReport = {
			turn: 1,
			mapWidth: 1000,
			mapHeight: 1000,
			planetCount: 1,
			planets: [makePlanet({ number: 1, x: 100, y: 100 })],
			race: "Earthlings",
			localShipClass: [],
			routes: [],
			localPlayerDrive: 1,
			localPlayerWeapons: 0,
			localPlayerShields: 0,
			localPlayerCargo: 0,
			...EMPTY_SHIP_GROUPS,
		};
		expect(buildCargoRouteLines(report)).toEqual([]);
	});

	test("arrowhead wings symmetric around the shaft direction", () => {
		const report = makeReport(
			[
				makePlanet({ number: 1, x: 0, y: 0 }),
				makePlanet({ number: 2, x: 100, y: 0 }),
			],
			1,
			[{ loadType: "COL", destinationPlanetNumber: 2 }],
		);
		const [shaft, leftWing, rightWing] = buildCargoRouteLines(report);
		expect(shaft).toBeDefined();
		expect(leftWing).toBeDefined();
		expect(rightWing).toBeDefined();
		if (
			shaft === undefined ||
			leftWing === undefined ||
			rightWing === undefined
		)
			return;
		// Both wings start at the head.
		expect(leftWing.x1).toBe(shaft.x2);
		expect(leftWing.y1).toBe(shaft.y2);
		expect(rightWing.x1).toBe(shaft.x2);
		expect(rightWing.y1).toBe(shaft.y2);
		// And land symmetrically around the y axis (shaft along +x).
		expect(leftWing.y2 + rightWing.y2).toBeCloseTo(0);
		expect(leftWing.x2).toBeCloseTo(rightWing.x2);
	});
});
