<!--
Planet inspector. Renders the documented field set for each planet
kind (local / other / uninhabited / unidentified) and exposes a
Rename action on owned (`local`) planets that opens an inline
editor. The editor runs the same `validateEntityName` rules as the
server-side validator (parity with `pkg/util/string.go`) and, on
confirm, appends a `planetRename` command to the local order draft
through the `OrderDraftStore` provided via context.

The read-only path stays unchanged for non-`local` planets. The
inline editor lives directly inside this component per PLAN.md
Phase 14 — a separate file would be over-abstraction for one input
field with five buttons.
-->
<script lang="ts">
	import { getContext, tick } from "svelte";
	import type {
		ReportLocalShipGroup,
		ReportOtherShipGroup,
		ReportPlanet,
		ReportRoute,
		ScienceSummary,
		ShipClassSummary,
	} from "../../api/game-state";
	import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
	import {
		ORDER_DRAFT_CONTEXT_KEY,
		OrderDraftStore,
	} from "../../sync/order-draft.svelte";
	import {
		validateEntityName,
		type EntityNameInvalidReason,
	} from "$lib/util/entity-name";
	import { formatFloat } from "$lib/util/number-format";
	import CargoRoutes from "./planet/cargo-routes.svelte";
	import Production from "./planet/production.svelte";
	import ShipGroups from "./planet/ship-groups.svelte";

	type Props = {
		planet: ReportPlanet;
		localShipClass: ShipClassSummary[];
		localScience: ScienceSummary[];
		routes: ReportRoute[];
		planets: ReportPlanet[];
		mapWidth: number;
		mapHeight: number;
		localPlayerDrive: number;
		localShipGroups: ReportLocalShipGroup[];
		otherShipGroups: ReportOtherShipGroup[];
		localRace: string;
	};
	let {
		planet,
		localShipClass,
		localScience,
		routes,
		planets,
		mapWidth,
		mapHeight,
		localPlayerDrive,
		localShipGroups,
		otherShipGroups,
		localRace,
	}: Props = $props();

	const kindKeyMap: Record<ReportPlanet["kind"], TranslationKey> = {
		local: "game.inspector.planet.kind.local",
		other: "game.inspector.planet.kind.other",
		uninhabited: "game.inspector.planet.kind.uninhabited",
		unidentified: "game.inspector.planet.kind.unidentified",
	};

	const invalidReasonKeyMap: Record<EntityNameInvalidReason, TranslationKey> = {
		empty: "game.inspector.planet.rename.invalid.empty",
		too_long: "game.inspector.planet.rename.invalid.too_long",
		starts_with_special:
			"game.inspector.planet.rename.invalid.starts_with_special",
		ends_with_special: "game.inspector.planet.rename.invalid.ends_with_special",
		consecutive_specials:
			"game.inspector.planet.rename.invalid.consecutive_specials",
		whitespace: "game.inspector.planet.rename.invalid.whitespace",
		disallowed_character:
			"game.inspector.planet.rename.invalid.disallowed_character",
	};

	const draft = getContext<OrderDraftStore | undefined>(
		ORDER_DRAFT_CONTEXT_KEY,
	);

	let renameOpen = $state(false);
	let renameInput = $state("");
	let inputEl: HTMLInputElement | null = $state(null);

	const renameValidation = $derived(validateEntityName(renameInput));
	const renameInvalidMessage = $derived(
		renameValidation.ok
			? ""
			: i18n.t(invalidReasonKeyMap[renameValidation.reason]),
	);

	const kindLabel = $derived(i18n.t(kindKeyMap[planet.kind]));
	const coordinates = $derived(
		`${formatFloat(planet.x)}, ${formatFloat(planet.y)}`,
	);
	const productionLabel = $derived(productionDisplay(planet.production));

	function productionDisplay(value: string | null): string {
		if (value === null || value === "") {
			return i18n.t("game.inspector.planet.production_none");
		}
		return value;
	}

	async function openRename(): Promise<void> {
		renameInput = planet.name;
		renameOpen = true;
		await tick();
		inputEl?.focus();
		inputEl?.select();
	}

	function cancelRename(): void {
		renameOpen = false;
		renameInput = "";
	}

	async function confirmRename(): Promise<void> {
		const result = validateEntityName(renameInput);
		if (!result.ok || draft === undefined) return;
		await draft.add({
			kind: "planetRename",
			id: crypto.randomUUID(),
			planetNumber: planet.number,
			name: result.value,
		});
		renameOpen = false;
		renameInput = "";
	}

	function onKeyDown(event: KeyboardEvent): void {
		if (event.key === "Escape") {
			event.preventDefault();
			cancelRename();
			return;
		}
		if (event.key === "Enter") {
			event.preventDefault();
			void confirmRename();
		}
	}
</script>

<section
	class="inspector"
	data-testid="inspector-planet"
	data-planet-id={planet.number}
	data-planet-kind={planet.kind}
>
	<header>
		<p class="kind" data-testid="inspector-planet-kind">{kindLabel}</p>
		{#if planet.kind !== "unidentified"}
			<h3 class="name" data-testid="inspector-planet-name">{planet.name}</h3>
		{/if}
		{#if planet.kind === "local" && !renameOpen}
			<button
				type="button"
				class="action"
				data-testid="inspector-planet-rename-action"
				onclick={openRename}
			>
				{i18n.t("game.inspector.planet.action.rename")}
			</button>
		{/if}
	</header>

	{#if planet.kind === "local" && renameOpen}
		<div class="rename" data-testid="inspector-planet-rename">
			<label class="rename-label" for="planet-rename-input">
				{i18n.t("game.inspector.planet.rename.title")}
			</label>
			<input
				id="planet-rename-input"
				type="text"
				class="rename-input"
				data-testid="inspector-planet-rename-input"
				bind:value={renameInput}
				bind:this={inputEl}
				onkeydown={onKeyDown}
				aria-invalid={renameValidation.ok ? "false" : "true"}
				aria-describedby={renameValidation.ok ? undefined : "planet-rename-error"}
			/>
			{#if !renameValidation.ok}
				<p
					id="planet-rename-error"
					class="rename-error"
					data-testid="inspector-planet-rename-error"
				>
					{renameInvalidMessage}
				</p>
			{/if}
			<div class="rename-actions">
				<button
					type="button"
					class="rename-cancel"
					data-testid="inspector-planet-rename-cancel"
					onclick={cancelRename}
				>
					{i18n.t("game.inspector.planet.rename.cancel")}
				</button>
				<button
					type="button"
					class="rename-confirm"
					data-testid="inspector-planet-rename-confirm"
					disabled={!renameValidation.ok || draft === undefined}
					onclick={() => void confirmRename()}
				>
					{i18n.t("game.inspector.planet.rename.confirm")}
				</button>
			</div>
		</div>
	{/if}

	{#if planet.kind === "local"}
		<Production {planet} {localShipClass} {localScience} />
		<CargoRoutes
			{planet}
			{routes}
			{planets}
			{mapWidth}
			{mapHeight}
			{localPlayerDrive}
		/>
	{/if}

	<ShipGroups
		{planet}
		{localShipGroups}
		{otherShipGroups}
		{localRace}
	/>

	<dl class="fields">
		{#if planet.kind === "other" && planet.owner !== null}
			<div class="field" data-testid="inspector-planet-field-owner">
				<dt>{i18n.t("game.inspector.planet.field.owner")}</dt>
				<dd>{planet.owner}</dd>
			</div>
		{/if}

		<div class="field" data-testid="inspector-planet-field-coordinates">
			<dt>{i18n.t("game.inspector.planet.field.coordinates")}</dt>
			<dd class="numeric">{coordinates}</dd>
		</div>

		{#if planet.size !== null}
			<div class="field" data-testid="inspector-planet-field-size">
				<dt>{i18n.t("game.inspector.planet.field.size")}</dt>
				<dd class="numeric">{formatFloat(planet.size)}</dd>
			</div>
		{/if}

		{#if planet.resources !== null}
			<div class="field" data-testid="inspector-planet-field-natural_resources">
				<dt>{i18n.t("game.inspector.planet.field.natural_resources")}</dt>
				<dd class="numeric">{formatFloat(planet.resources)}</dd>
			</div>
		{/if}

		{#if planet.population !== null}
			<div class="field" data-testid="inspector-planet-field-population">
				<dt>{i18n.t("game.inspector.planet.field.population")}</dt>
				<dd class="numeric">{formatFloat(planet.population)}</dd>
			</div>
		{/if}

		{#if planet.colonists !== null}
			<div class="field" data-testid="inspector-planet-field-colonists">
				<dt>{i18n.t("game.inspector.planet.field.colonists")}</dt>
				<dd class="numeric">{formatFloat(planet.colonists)}</dd>
			</div>
		{/if}

		{#if planet.industry !== null}
			<div class="field" data-testid="inspector-planet-field-industry">
				<dt>{i18n.t("game.inspector.planet.field.industry")}</dt>
				<dd class="numeric">{formatFloat(planet.industry)}</dd>
			</div>
		{/if}

		{#if planet.industryStockpile !== null}
			<div class="field" data-testid="inspector-planet-field-industry_stockpile">
				<dt>{i18n.t("game.inspector.planet.field.industry_stockpile")}</dt>
				<dd class="numeric">{formatFloat(planet.industryStockpile)}</dd>
			</div>
		{/if}

		{#if planet.materialsStockpile !== null}
			<div class="field" data-testid="inspector-planet-field-materials_stockpile">
				<dt>{i18n.t("game.inspector.planet.field.materials_stockpile")}</dt>
				<dd class="numeric">{formatFloat(planet.materialsStockpile)}</dd>
			</div>
		{/if}

		{#if planet.production !== null && planet.kind !== "local"}
			<div class="field" data-testid="inspector-planet-field-production">
				<dt>{i18n.t("game.inspector.planet.field.production")}</dt>
				<dd>{productionLabel}</dd>
			</div>
		{/if}

		{#if planet.freeIndustry !== null}
			<div class="field" data-testid="inspector-planet-field-free_industry">
				<dt>{i18n.t("game.inspector.planet.field.free_industry")}</dt>
				<dd class="numeric">{formatFloat(planet.freeIndustry)}</dd>
			</div>
		{/if}
	</dl>

	{#if planet.kind === "unidentified"}
		<p class="hint" data-testid="inspector-planet-no-data">
			{i18n.t("game.inspector.planet.unidentified_no_data")}
		</p>
	{/if}
</section>

<style>
	.inspector {
		padding: 1rem;
		font-family: system-ui, sans-serif;
		display: flex;
		flex-direction: column;
		gap: 0.75rem;
	}
	header {
		display: flex;
		flex-direction: column;
		gap: 0.15rem;
	}
	.kind {
		margin: 0;
		font-size: 0.75rem;
		text-transform: uppercase;
		letter-spacing: 0.05em;
		color: var(--color-text-muted);
	}
	.name {
		margin: 0;
		font-size: 1.05rem;
	}
	.fields {
		margin: 0;
		display: grid;
		grid-template-columns: max-content 1fr;
		row-gap: 0.25rem;
		column-gap: 0.75rem;
	}
	.field {
		display: contents;
	}
	.field dt {
		color: var(--color-text-muted);
		font-size: 0.8rem;
	}
	.field dd {
		margin: 0;
		font-variant-numeric: tabular-nums;
		font-size: 0.85rem;
	}
	.field dd.numeric {
		font-family: var(--font-mono);
	}
	.hint {
		margin: 0;
		color: var(--color-text-muted);
		font-size: 0.8rem;
	}
	.action {
		align-self: flex-start;
		margin-top: 0.25rem;
		font: inherit;
		font-size: 0.85rem;
		padding: 0.2rem 0.55rem;
		background: transparent;
		color: var(--color-text-muted);
		border: 1px solid var(--color-border);
		border-radius: 3px;
		cursor: pointer;
	}
	.action:hover {
		color: var(--color-text);
		border-color: var(--color-accent);
	}
	.rename {
		display: flex;
		flex-direction: column;
		gap: 0.35rem;
	}
	.rename-label {
		font-size: 0.85rem;
		color: var(--color-text-muted);
	}
	.rename-input {
		font: inherit;
		padding: 0.3rem 0.5rem;
		background: var(--color-bg);
		color: var(--color-text);
		border: 1px solid var(--color-border);
		border-radius: 3px;
	}
	.rename-input[aria-invalid="true"] {
		border-color: var(--color-danger);
	}
	.rename-error {
		margin: 0;
		font-size: 0.8rem;
		color: var(--color-danger);
	}
	.rename-actions {
		display: flex;
		gap: 0.4rem;
	}
	.rename-cancel,
	.rename-confirm {
		font: inherit;
		font-size: 0.85rem;
		padding: 0.25rem 0.65rem;
		background: transparent;
		color: var(--color-text-muted);
		border: 1px solid var(--color-border);
		border-radius: 3px;
		cursor: pointer;
	}
	.rename-confirm:not(:disabled):hover,
	.rename-cancel:hover {
		color: var(--color-text);
		border-color: var(--color-accent);
	}
	.rename-confirm:disabled {
		cursor: not-allowed;
		opacity: 0.5;
	}
</style>
