<!--
Planet inspector. Renders the documented field set for each planet
kind (local / other / uninhabited / unidentified) and exposes a
click-to-edit affordance on the name itself for owned (`local`)
planets: a click on the name turns it into an inline input with a
single ✓ confirm icon (Escape cancels). 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 — a separate
file would be over-abstraction for one input field and a confirm
button. F8-05 (issue #48 п.13) dropped the separate `Rename`
action button and the explicit `Cancel` button: the name itself is
the entry point, Escape (or unmounting the inspector) reverts.
-->
<script lang="ts">
	import { getContext, tick } from "svelte";
	import type {
		ReportBombing,
		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 { scrollToBombingRow } from "$lib/report-nav";
	import { activeView } from "$lib/app-nav.svelte";
	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;
		bombing?: ReportBombing | null;
	};
	let {
		planet,
		localShipClass,
		localScience,
		routes,
		planets,
		mapWidth,
		mapHeight,
		localPlayerDrive,
		localShipGroups,
		otherShipGroups,
		localRace,
		bombing = null,
	}: Props = $props();

	function openBombingReport(): void {
		if (bombing === null) return;
		activeView.select("report");
		scrollToBombingRow(bombing.planetNumber);
	}

	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 === "local"}
			{#if renameOpen}
				<div class="rename" data-testid="inspector-planet-rename">
					<input
						id="planet-rename-input"
						type="text"
						class="rename-input"
						data-testid="inspector-planet-rename-input"
						aria-label={i18n.t("game.inspector.planet.rename.title")}
						bind:value={renameInput}
						bind:this={inputEl}
						onkeydown={onKeyDown}
						aria-invalid={renameValidation.ok ? "false" : "true"}
						aria-describedby={renameValidation.ok ? undefined : "planet-rename-error"}
					/>
					<button
						type="button"
						class="icon-action icon-action--apply"
						data-testid="inspector-planet-rename-confirm"
						disabled={!renameValidation.ok || draft === undefined}
						aria-label={i18n.t("game.inspector.planet.rename.confirm")}
						onclick={() => void confirmRename()}
					>
						<span aria-hidden="true">✓</span>
					</button>
				</div>
				{#if !renameValidation.ok}
					<p
						id="planet-rename-error"
						class="rename-error"
						data-testid="inspector-planet-rename-error"
					>
						{renameInvalidMessage}
					</p>
				{/if}
			{:else}
				<button
					type="button"
					class="name name--editable"
					data-testid="inspector-planet-name"
					aria-label={i18n.t("game.inspector.planet.action.rename")}
					onclick={openRename}
				>
					{planet.name}
				</button>
			{/if}
		{:else if planet.kind !== "unidentified"}
			<h3 class="name" data-testid="inspector-planet-name">{planet.name}</h3>
		{/if}
	</header>

	{#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}

	{#if bombing !== null}
		<button
			type="button"
			class="bombing-link"
			class:bombing-link--wiped={bombing.wiped}
			data-testid="inspector-planet-view-bombing"
			data-bombing-wiped={bombing.wiped ? "true" : "false"}
			onclick={openBombingReport}
		>
			{i18n.t(
				bombing.wiped
					? "game.inspector.planet.view_bombing_wiped"
					: "game.inspector.planet.view_bombing",
			)}
		</button>
	{/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;
	}
	.name--editable {
		font: inherit;
		font-size: 1.05rem;
		font-weight: 600;
		text-align: left;
		padding: 0.1rem 0.25rem;
		margin: 0 -0.25rem;
		background: transparent;
		color: inherit;
		border: 1px dashed transparent;
		border-radius: 3px;
		cursor: text;
	}
	.name--editable:hover,
	.name--editable:focus-visible {
		border-color: var(--color-border);
		background: var(--color-surface-hover);
	}
	.rename {
		display: flex;
		align-items: center;
		gap: 0.4rem;
	}
	.rename-input {
		flex: 1;
		min-width: 0;
		font: inherit;
		font-size: 1rem;
		padding: 0.25rem 0.45rem;
		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.2rem 0 0 0;
		font-size: 0.8rem;
		color: var(--color-danger);
	}
	.icon-action {
		flex: 0 0 auto;
		font: inherit;
		font-size: 0.95rem;
		line-height: 1;
		padding: 0.25rem 0.5rem;
		background: transparent;
		color: var(--color-text-muted);
		border: 1px solid var(--color-border);
		border-radius: 3px;
		cursor: pointer;
	}
	.icon-action:not(:disabled):hover {
		color: var(--color-text);
		border-color: var(--color-accent);
	}
	.icon-action:disabled {
		cursor: not-allowed;
		opacity: 0.5;
	}
	.icon-action--apply:not(:disabled) {
		color: var(--color-success);
	}
	.bombing-link {
		font: inherit;
		font-size: 0.85rem;
		padding: 0.4rem 0.6rem;
		background: var(--color-surface-hover);
		color: var(--color-text);
		border: 1px solid var(--color-border);
		border-left: 3px solid var(--color-warning, #f57f17);
		border-radius: 3px;
		cursor: pointer;
		text-align: left;
	}
	.bombing-link:hover,
	.bombing-link:focus-visible {
		border-color: var(--color-accent);
		border-left-color: var(--color-warning, #f57f17);
	}
	.bombing-link--wiped {
		border-left-color: var(--color-danger);
	}
	.bombing-link--wiped:hover,
	.bombing-link--wiped:focus-visible {
		border-left-color: var(--color-danger);
	}
</style>
