// Phase 27 battle and bombing markers on the map.
//
// Two visual markers per planet:
//
//   * Battle marker — an X cross drawn through the corners of the
//     square that circumscribes the planet circle. Two yellow
//     LinePrim, stroke width scales linearly with the number of
//     shots: 1 shot → 1px, 100+ shots → 5px (capped). Clicking
//     either line opens the Battle Viewer for the corresponding
//     UUID.
//   * Bombing marker — a thin stroke-only circle slightly larger
//     than the planet circle. Yellow on damaged planets, red on
//     wiped planets. Clicking it deep-links to the bombings row in
//     the Reports view for the planet number.
//
// Both markers are wired into `state-binding.ts` so they live in the
// same `world` / `hitLookup` plumbing as planets and ship groups.

import type { GameReport, ReportPlanet } from "../api/game-state";
import type {
	CirclePrim,
	LinePrim,
	Primitive,
	PrimitiveID,
	Style,
} from "./world";

export const BATTLE_MARKER_COLOR = 0xffd400;
export const BOMBING_MARKER_COLOR_DAMAGED = 0xffd400;
export const BOMBING_MARKER_COLOR_WIPED = 0xff3030;

/** Battle and bombing marker primitive ids use a high-bit prefix to
 * avoid colliding with planet numbers or cargo-route line ids. */
export const BATTLE_MARKER_ID_PREFIX = 0xa0000000;
export const BOMBING_MARKER_ID_PREFIX = 0xc0000000;

const PLANET_RADIUS_WORLD = 6;
const BOMBING_RING_RADIUS = PLANET_RADIUS_WORLD + 3;
const BATTLE_CROSS_HALF = PLANET_RADIUS_WORLD + 2;

/** Battle marker priority sits between planets (1..4) and cargo
 * routes; the cross is over the planet but loses clicks against the
 * planet glyph itself. */
const BATTLE_MARKER_PRIORITY = 9;
const BOMBING_MARKER_PRIORITY = 10;

const BATTLE_LINE_INDEX_A = 0;
const BATTLE_LINE_INDEX_B = 1;

export interface BattleMarkerTarget {
	kind: "battle";
	battleId: string;
	planet: number;
}

export interface BombingMarkerTarget {
	kind: "bombing";
	planet: number;
}

export type MarkerTarget = BattleMarkerTarget | BombingMarkerTarget;

export interface BuildMarkersResult {
	primitives: Primitive[];
	lookup: Map<PrimitiveID, MarkerTarget>;
}

/**
 * battleMarkerStrokeWidth maps a battle's `shots` count to a stroke
 * width in pixels. 1 shot → 1 px (the thinnest visible), 100+ shots
 * → 5 px (the cap). Linearly interpolated between those bounds.
 */
export function battleMarkerStrokeWidth(shots: number): number {
	if (shots <= 1) return 1;
	if (shots >= 100) return 5;
	return 1 + ((shots - 1) * 4) / 99;
}

/**
 * buildBattleAndBombingMarkers emits battle and bombing marker
 * primitives plus a hit-lookup mapping for the current-turn report.
 * Battles whose planet is not visible (e.g. observer-only without a
 * report.planets entry) are skipped — they have no on-map location
 * to anchor against.
 */
export function buildBattleAndBombingMarkers(
	report: GameReport,
): BuildMarkersResult {
	const planetByNumber = new Map<number, ReportPlanet>();
	for (const planet of report.planets) {
		planetByNumber.set(planet.number, planet);
	}

	const primitives: Primitive[] = [];
	const lookup = new Map<PrimitiveID, MarkerTarget>();

	for (let i = 0; i < report.battles.length; i++) {
		const battle = report.battles[i];
		const planet = planetByNumber.get(battle.planet);
		if (planet === undefined) continue;
		const strokeWidthPx = battleMarkerStrokeWidth(battle.shots);
		const style: Style = {
			strokeColor: BATTLE_MARKER_COLOR,
			strokeAlpha: 0.95,
			strokeWidthPx,
		};
		const baseId = BATTLE_MARKER_ID_PREFIX | (i << 4);
		const lineA: LinePrim = {
			kind: "line",
			id: baseId | BATTLE_LINE_INDEX_A,
			priority: BATTLE_MARKER_PRIORITY,
			style,
			hitSlopPx: 0,
			x1: planet.x - BATTLE_CROSS_HALF,
			y1: planet.y - BATTLE_CROSS_HALF,
			x2: planet.x + BATTLE_CROSS_HALF,
			y2: planet.y + BATTLE_CROSS_HALF,
		};
		const lineB: LinePrim = {
			kind: "line",
			id: baseId | BATTLE_LINE_INDEX_B,
			priority: BATTLE_MARKER_PRIORITY,
			style,
			hitSlopPx: 0,
			x1: planet.x - BATTLE_CROSS_HALF,
			y1: planet.y + BATTLE_CROSS_HALF,
			x2: planet.x + BATTLE_CROSS_HALF,
			y2: planet.y - BATTLE_CROSS_HALF,
		};
		const target: BattleMarkerTarget = {
			kind: "battle",
			battleId: battle.id,
			planet: battle.planet,
		};
		primitives.push(lineA, lineB);
		lookup.set(lineA.id, target);
		lookup.set(lineB.id, target);
	}

	for (let i = 0; i < report.bombings.length; i++) {
		const bombing = report.bombings[i];
		const planet = planetByNumber.get(bombing.planetNumber);
		if (planet === undefined) continue;
		const color = bombing.wiped
			? BOMBING_MARKER_COLOR_WIPED
			: BOMBING_MARKER_COLOR_DAMAGED;
		const style: Style = {
			strokeColor: color,
			strokeAlpha: 0.9,
			strokeWidthPx: 1.5,
		};
		const id = BOMBING_MARKER_ID_PREFIX | i;
		const ring: CirclePrim = {
			kind: "circle",
			id,
			priority: BOMBING_MARKER_PRIORITY,
			style,
			hitSlopPx: 0,
			x: planet.x,
			y: planet.y,
			radius: BOMBING_RING_RADIUS,
		};
		primitives.push(ring);
		lookup.set(id, { kind: "bombing", planet: bombing.planetNumber });
	}

	return { primitives, lookup };
}
