// Phase 28 — FBS BattleReport payload builder used by Battle Viewer
// Playwright specs. Mirrors the `user.games.battle` ConnectRPC
// response that the gateway re-encodes from the engine's JSON.

import { Builder } from "flatbuffers";

import {
	BattleActionReport,
	BattleReport,
	BattleReportGroup,
	RaceEntry,
	ShipEntry,
	TechEntry,
	UUID,
} from "../../../src/proto/galaxy/fbs/battle";

export interface BattleGroupFixture {
	key: number;
	race: string;
	className: string;
	tech?: Record<string, number>;
	num: number;
	numLeft: number;
	loadType?: string;
	loadQuantity?: number;
	inBattle?: boolean;
}

export interface BattleActionFixture {
	a: number;
	sa: number;
	d: number;
	sd: number;
	x: boolean;
}

export interface BattleFixture {
	id: string;
	planet: number;
	planetName: string;
	races: Record<string, string>;
	ships: BattleGroupFixture[];
	protocol: BattleActionFixture[];
}

export function buildBattlePayload(fixture: BattleFixture): Uint8Array {
	const builder = new Builder(512);

	const planetNameOff = builder.createString(fixture.planetName);

	const raceOffsets: number[] = [];
	for (const [keyStr, value] of Object.entries(fixture.races)) {
		const key = Number(keyStr);
		const [hi, lo] = uuidToHiLo(value);
		RaceEntry.startRaceEntry(builder);
		RaceEntry.addKey(builder, BigInt(key));
		RaceEntry.addValue(builder, UUID.createUUID(builder, hi, lo));
		raceOffsets.push(RaceEntry.endRaceEntry(builder));
	}
	const racesVec = BattleReport.createRacesVector(builder, raceOffsets);

	const shipOffsets: number[] = [];
	for (const ship of fixture.ships) {
		const techOffsets: number[] = [];
		for (const [tk, tv] of Object.entries(ship.tech ?? {})) {
			const tkOff = builder.createString(tk);
			TechEntry.startTechEntry(builder);
			TechEntry.addKey(builder, tkOff);
			TechEntry.addValue(builder, tv);
			techOffsets.push(TechEntry.endTechEntry(builder));
		}
		const techVec = BattleReportGroup.createTechVector(builder, techOffsets);
		const raceStrOff = builder.createString(ship.race);
		const classNameOff = builder.createString(ship.className);
		const loadTypeOff = builder.createString(ship.loadType ?? "");
		BattleReportGroup.startBattleReportGroup(builder);
		BattleReportGroup.addInBattle(builder, ship.inBattle ?? true);
		BattleReportGroup.addNumber(builder, BigInt(ship.num));
		BattleReportGroup.addNumberLeft(builder, BigInt(ship.numLeft));
		BattleReportGroup.addLoadQuantity(builder, ship.loadQuantity ?? 0);
		BattleReportGroup.addTech(builder, techVec);
		BattleReportGroup.addRace(builder, raceStrOff);
		BattleReportGroup.addClassName(builder, classNameOff);
		BattleReportGroup.addLoadType(builder, loadTypeOff);
		const groupOff = BattleReportGroup.endBattleReportGroup(builder);
		ShipEntry.startShipEntry(builder);
		ShipEntry.addKey(builder, BigInt(ship.key));
		ShipEntry.addValue(builder, groupOff);
		shipOffsets.push(ShipEntry.endShipEntry(builder));
	}
	const shipsVec = BattleReport.createShipsVector(builder, shipOffsets);

	const protocolOffsets: number[] = [];
	for (const action of fixture.protocol) {
		BattleActionReport.startBattleActionReport(builder);
		BattleActionReport.addAttacker(builder, BigInt(action.a));
		BattleActionReport.addAttackerShipClass(builder, BigInt(action.sa));
		BattleActionReport.addDefender(builder, BigInt(action.d));
		BattleActionReport.addDefenderShipClass(builder, BigInt(action.sd));
		BattleActionReport.addDestroyed(builder, action.x);
		protocolOffsets.push(BattleActionReport.endBattleActionReport(builder));
	}
	const protocolVec = BattleReport.createProtocolVector(
		builder,
		protocolOffsets,
	);

	const [idHi, idLo] = uuidToHiLo(fixture.id);
	BattleReport.startBattleReport(builder);
	BattleReport.addId(builder, UUID.createUUID(builder, idHi, idLo));
	BattleReport.addPlanet(builder, BigInt(fixture.planet));
	BattleReport.addPlanetName(builder, planetNameOff);
	BattleReport.addRaces(builder, racesVec);
	BattleReport.addShips(builder, shipsVec);
	BattleReport.addProtocol(builder, protocolVec);
	builder.finish(BattleReport.endBattleReport(builder));
	return builder.asUint8Array();
}

function uuidToHiLo(value: string): [bigint, bigint] {
	const hex = value.replace(/-/g, "").toLowerCase();
	if (hex.length !== 32 || /[^0-9a-f]/.test(hex)) {
		throw new Error(`buildBattlePayload: invalid uuid ${value}`);
	}
	const hi = BigInt(`0x${hex.slice(0, 16)}`);
	const lo = BigInt(`0x${hex.slice(16, 32)}`);
	return [hi, lo];
}
