<!--
F8-05 production-controls subsection of the planet inspector. Renders
two dropdowns on a single row (primary: industry / materials /
research / ship; secondary: tech / science / ship class — only for
the research and ship contexts) together with green ✓ apply and
yellow ✗ cancel icon buttons. The apply button becomes enabled when
the row selection differs from the planet's current effective
production (post-overlay) and the choice is complete (industry /
materials need no secondary, research / ship require one). Apply
appends a `setProductionType` command to the local order draft via
`OrderDraftStore`; the collapse-by-`planetNumber` rule inside `add`
keeps at most one production choice per planet. Cancel resets the
local row state to the current effective value — it does **not**
revoke an in-flight command (that is the Order tab's responsibility).

Until F8-05 every leaf click auto-submitted; the apply gate is the
new shape. The active-on-row state is derived from `planet.production`
through a parser that mirrors the engine's
`Cache.PlanetProductionDisplayName` mapping. Whenever the inspector
switches to a different planet or `planet.production` itself changes
(typically after a successful apply round-trip projected by
`applyOrderOverlay`), the row re-seeds from the new parsed value.

The Research target list combines the four tech display strings with
each defined science from `localScience`. A science name shadows the
tech display string with the same text — the engine sends a single
ambiguous display string in `planet.production`; user-defined
sciences win because they carry more user intent.
-->
<script lang="ts">
	import { getContext } from "svelte";
	import type {
		ReportPlanet,
		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 type { ProductionType } from "../../../sync/order-types";

	type Props = {
		planet: ReportPlanet;
		localShipClass: ShipClassSummary[];
		localScience: ScienceSummary[];
	};
	let { planet, localShipClass, localScience }: Props = $props();

	type MainSegment = "industry" | "materials" | "research" | "ship";

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

	type TechFbs = "DRIVE" | "WEAPONS" | "SHIELDS" | "CARGO";

	const RESEARCH_OPTIONS: ReadonlyArray<{
		fbs: TechFbs;
		slug: "drive" | "weapons" | "shields" | "cargo";
		labelKey: TranslationKey;
	}> = [
		{
			fbs: "DRIVE",
			slug: "drive",
			labelKey: "game.inspector.planet.production.research.drive",
		},
		{
			fbs: "WEAPONS",
			slug: "weapons",
			labelKey: "game.inspector.planet.production.research.weapons",
		},
		{
			fbs: "SHIELDS",
			slug: "shields",
			labelKey: "game.inspector.planet.production.research.shields",
		},
		{
			fbs: "CARGO",
			slug: "cargo",
			labelKey: "game.inspector.planet.production.research.cargo",
		},
	];

	function parseMain(
		value: string | null,
		classes: ShipClassSummary[],
		sciences: ScienceSummary[],
	): MainSegment | null {
		if (value === null || value === "" || value === "-") return null;
		// Sciences first: a user-named "Drive" science wins over the
		// Drive tech display string. If no science matches, fall through
		// to tech / ship class detection.
		if (sciences.some((s) => s.name === value)) return "research";
		switch (value) {
			case "Capital":
				return "industry";
			case "Material":
				return "materials";
			case "Drive":
			case "Weapons":
			case "Shields":
			case "Cargo":
				return "research";
		}
		return classes.some((c) => c.name === value) ? "ship" : null;
	}

	function parseTarget(
		value: string | null,
		classes: ShipClassSummary[],
		sciences: ScienceSummary[],
	): string | null {
		if (value === null || value === "") return null;
		// Science wins over tech display string (same shadowing rule).
		if (sciences.some((s) => s.name === value)) return value;
		switch (value) {
			case "Drive":
				return "DRIVE";
			case "Weapons":
				return "WEAPONS";
			case "Shields":
				return "SHIELDS";
			case "Cargo":
				return "CARGO";
		}
		return classes.some((c) => c.name === value) ? value : null;
	}

	const parsedMain = $derived(
		parseMain(planet.production, localShipClass, localScience),
	);
	const parsedTarget = $derived(
		parseTarget(planet.production, localShipClass, localScience),
	);

	// An owned planet always produces something on the engine side, so
	// the row defaults to `industry` when `planet.production` is
	// somehow null/empty rather than carrying a separate "(none)"
	// placeholder option. The user lands on a valid production type
	// and can apply or change immediately.
	const DEFAULT_MAIN: MainSegment = "industry";

	let mainSel = $state<MainSegment>(DEFAULT_MAIN);
	let targetSel = $state<string>("");

	$effect(() => {
		// Reset row state whenever the inspector switches to a different
		// planet or the effective production changes (e.g. after a
		// successful apply projected by the overlay). Reads are tracked
		// even though the assignments do not consume the values.
		void planet.number;
		void parsedMain;
		void parsedTarget;
		mainSel = parsedMain ?? DEFAULT_MAIN;
		targetSel = parsedTarget ?? "";
	});

	const needsTarget = $derived(mainSel === "research" || mainSel === "ship");

	const dirty = $derived(
		mainSel !== parsedMain
			|| (targetSel === "" ? null : targetSel) !== parsedTarget,
	);

	const applyDisabled = $derived(
		disabled
			|| (needsTarget && targetSel === "")
			|| !dirty,
	);

	const cancelDisabled = $derived(disabled || !dirty);

	function pickMain(event: Event): void {
		mainSel = (event.target as HTMLSelectElement).value as MainSegment;
		// Switching the primary list clears any pending secondary
		// choice — the picker for the new main might not even include
		// the previous target.
		targetSel = "";
	}

	function pickTarget(event: Event): void {
		targetSel = (event.target as HTMLSelectElement).value;
	}

	async function applyRow(): Promise<void> {
		if (applyDisabled || draft === undefined) return;
		if (mainSel === "industry") {
			await emit("CAP", "");
			return;
		}
		if (mainSel === "materials") {
			await emit("MAT", "");
			return;
		}
		if (mainSel === "research") {
			const tech = RESEARCH_OPTIONS.find((o) => o.fbs === targetSel);
			if (tech !== undefined) {
				await emit(tech.fbs, "");
			} else {
				await emit("SCIENCE", targetSel);
			}
			return;
		}
		if (mainSel === "ship") {
			await emit("SHIP", targetSel);
		}
	}

	function cancelRow(): void {
		mainSel = parsedMain ?? DEFAULT_MAIN;
		targetSel = parsedTarget ?? "";
	}

	async function emit(
		productionType: ProductionType,
		subject: string,
	): Promise<void> {
		if (draft === undefined) return;
		await draft.add({
			kind: "setProductionType",
			id: crypto.randomUUID(),
			planetNumber: planet.number,
			productionType,
			subject,
		});
	}
</script>

<section class="production" data-testid="inspector-planet-production">
	<h4 class="title">
		{i18n.t("game.inspector.planet.production.title")}
	</h4>
	<div class="row">
		<select
			class="select"
			data-testid="inspector-planet-production-main"
			aria-label={i18n.t("game.inspector.planet.production.main.aria")}
			value={mainSel}
			onchange={pickMain}
			{disabled}
		>
			<option value="industry">
				{i18n.t("game.inspector.planet.production.option.industry")}
			</option>
			<option value="materials">
				{i18n.t("game.inspector.planet.production.option.materials")}
			</option>
			<option value="research">
				{i18n.t("game.inspector.planet.production.option.research")}
			</option>
			<option value="ship">
				{i18n.t("game.inspector.planet.production.option.ship")}
			</option>
		</select>

		{#if needsTarget}
			<select
				class="select"
				data-testid="inspector-planet-production-target"
				aria-label={i18n.t(
					mainSel === "research"
						? "game.inspector.planet.production.target.research.aria"
						: "game.inspector.planet.production.target.ship.aria",
				)}
				value={targetSel}
				onchange={pickTarget}
				{disabled}
			>
				<option value="">
					{i18n.t(
						mainSel === "research"
							? "game.inspector.planet.production.target.research.placeholder"
							: "game.inspector.planet.production.target.ship.placeholder",
					)}
				</option>
				{#if mainSel === "research"}
					{#each RESEARCH_OPTIONS as option (option.fbs)}
						<option
							value={option.fbs}
							data-testid={`inspector-planet-production-target-option-${option.slug}`}
						>
							{i18n.t(option.labelKey)}
						</option>
					{/each}
					{#each localScience as sci (sci.name)}
						<option
							value={sci.name}
							data-testid={`inspector-planet-production-target-option-science-${sci.name}`}
						>
							{sci.name}
						</option>
					{/each}
				{:else if mainSel === "ship"}
					{#if localShipClass.length === 0}
						<option
							value=""
							disabled
							data-testid="inspector-planet-production-ship-empty"
						>
							{i18n.t("game.inspector.planet.production.ship.no_classes")}
						</option>
					{:else}
						{#each localShipClass as cls (cls.name)}
							<option
								value={cls.name}
								data-testid={`inspector-planet-production-target-option-ship-${cls.name}`}
							>
								{cls.name}
							</option>
						{/each}
					{/if}
				{/if}
			</select>
		{/if}

		<button
			type="button"
			class="icon-action icon-action--apply"
			data-testid="inspector-planet-production-apply"
			disabled={applyDisabled}
			aria-label={i18n.t("game.inspector.planet.production.apply")}
			onclick={() => void applyRow()}
		>
			<span aria-hidden="true">✓</span>
		</button>
		<button
			type="button"
			class="icon-action icon-action--cancel"
			data-testid="inspector-planet-production-cancel"
			disabled={cancelDisabled}
			aria-label={i18n.t("game.inspector.planet.production.cancel")}
			onclick={cancelRow}
		>
			<span aria-hidden="true">✗</span>
		</button>
	</div>
</section>

<style>
	.production {
		display: flex;
		flex-direction: column;
		gap: 0.4rem;
	}
	.title {
		margin: 0;
		font-size: 0.85rem;
		color: var(--color-text-muted);
		font-weight: 500;
	}
	.row {
		display: flex;
		align-items: center;
		gap: 0.35rem;
		flex-wrap: nowrap;
	}
	.select {
		flex: 1 1 6rem;
		min-width: 0;
		font: inherit;
		font-size: 0.85rem;
		padding: 0.2rem 0.4rem;
		background: var(--color-surface-raised);
		color: var(--color-text);
		border: 1px solid var(--color-border);
		border-radius: 3px;
		cursor: pointer;
	}
	.select:disabled {
		cursor: not-allowed;
		opacity: 0.5;
	}
	.icon-action {
		flex: 0 0 auto;
		font: inherit;
		font-size: 1rem;
		line-height: 1;
		padding: 0.15rem 0.2rem;
		background: transparent;
		color: var(--color-text-muted);
		border: 0;
		border-radius: 0;
		cursor: pointer;
	}
	.icon-action:not(:disabled):hover {
		color: var(--color-text);
	}
	.icon-action:disabled {
		cursor: not-allowed;
		opacity: 0.35;
	}
	.icon-action--apply:not(:disabled) {
		color: var(--color-success);
	}
	.icon-action--cancel:not(:disabled) {
		color: var(--color-warning);
	}
</style>
