<!--
Reusable "Ship Class design area": the five design blocks (drive,
armament, weapons, shields, cargo) plus the four tech levels they are
built with. Each tech defaults to the player's current level and shows a
lock icon once overridden; clicking the lock resets it. A block claimed
by an active goal-seek lock renders read-only with its own lock marker.
The component is presentational — the parent owns the state and the
calculator math — so the ship-group upgrade flow can reuse it later.
-->
<script lang="ts">
	import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
	import {
		shipClassFieldErrors,
		type ShipClassValueInvalidReason,
	} from "$lib/util/ship-class-validation";
	import type { ClaimedInput } from "./calc-model";

	export interface DesignBlocksState {
		drive: number;
		armament: number;
		weapons: number;
		shields: number;
		cargo: number;
	}
	export interface TechState {
		drive: number;
		weapons: number;
		shields: number;
		cargo: number;
	}
	export type TechKey = keyof TechState;

	type Props = {
		blocks: DesignBlocksState;
		// Blocks after goal-seek: the claimed block carries its solved
		// value, which is what the read-only computed cell displays.
		resolved: DesignBlocksState;
		techs: TechState;
		techOverridden: Record<TechKey, boolean>;
		computedInput?: ClaimedInput | null;
		blocksReadonly?: boolean;
		onTechInput: (key: TechKey) => void;
		onResetTech: (key: TechKey) => void;
	};
	let {
		blocks = $bindable(),
		resolved,
		techs = $bindable(),
		techOverridden,
		computedInput = null,
		blocksReadonly = false,
		onTechInput,
		onResetTech,
	}: Props = $props();

	const VALUE_REASON_KEY: Record<ShipClassValueInvalidReason, TranslationKey> = {
		drive_value: "game.calculator.invalid.drive_value",
		armament_value: "game.calculator.invalid.armament_value",
		armament_not_integer: "game.calculator.invalid.armament_not_integer",
		weapons_value: "game.calculator.invalid.weapons_value",
		shields_value: "game.calculator.invalid.shields_value",
		cargo_value: "game.calculator.invalid.cargo_value",
		armament_weapons_pair: "game.calculator.invalid.armament_weapons_pair",
		all_zero: "game.calculator.invalid.all_zero",
	};

	// Per-block validity (independent of which one failed first) so every
	// invalid input is highlighted, not only the first.
	const blockErrors = $derived(shipClassFieldErrors(blocks));

	function blockError(key: keyof DesignBlocksState): string {
		const reason = blockErrors[key];
		return reason === undefined ? "" : i18n.t(VALUE_REASON_KEY[reason]);
	}
	function techError(key: TechKey): string {
		return techs[key] < 0 ? i18n.t("game.calculator.invalid.tech_value") : "";
	}

	const BLOCK_ROWS: {
		key: keyof DesignBlocksState;
		label: () => string;
		step: string;
		tech: TechKey | null;
	}[] = [
		{ key: "drive", label: () => i18n.t("game.calculator.field.drive"), step: "0.01", tech: "drive" },
		{ key: "armament", label: () => i18n.t("game.calculator.field.armament"), step: "1", tech: null },
		{ key: "weapons", label: () => i18n.t("game.calculator.field.weapons"), step: "0.01", tech: "weapons" },
		{ key: "shields", label: () => i18n.t("game.calculator.field.shields"), step: "0.01", tech: "shields" },
		{ key: "cargo", label: () => i18n.t("game.calculator.field.cargo"), step: "0.01", tech: "cargo" },
	];
</script>

<div class="design" data-testid="calculator-design-area">
	<div class="cols">
		<span></span>
		<span class="col-head">{i18n.t("game.calculator.col.ship")}</span>
		<span class="col-head">{i18n.t("game.calculator.col.tech")}</span>
	</div>
	{#each BLOCK_ROWS as row (row.key)}
		{@const isComputed = computedInput === row.key}
		<div class="row">
			<span class="label">{row.label()}</span>
			{#if isComputed}
				<input
					class="ship"
					type="number"
					step={row.step}
					readonly
					value={resolved[row.key]}
					data-computed="true"
					data-testid={`calculator-block-${row.key}`}
					title={i18n.t("game.calculator.lock.reset")}
				/>
			{:else}
				<input
					class="ship"
					type="number"
					step={row.step}
					min="0"
					bind:value={blocks[row.key]}
					readonly={blocksReadonly}
					aria-invalid={blockError(row.key) !== "" ? "true" : "false"}
					title={blockError(row.key)}
					data-testid={`calculator-block-${row.key}`}
				/>
			{/if}
			{#if row.tech !== null}
				{@const techKey = row.tech}
				<span class="tech-cell">
					<input
						class="tech"
						type="number"
						step="0.001"
						min="0"
						bind:value={techs[techKey]}
						oninput={() => onTechInput(techKey)}
						aria-invalid={techError(techKey) !== "" ? "true" : "false"}
						title={techError(techKey)}
						data-testid={`calculator-tech-${techKey}`}
					/>
					{#if techOverridden[techKey]}
						<button
							type="button"
							class="lock"
							title={i18n.t("game.calculator.tech.reset")}
							aria-label={i18n.t("game.calculator.tech.reset")}
							data-testid={`calculator-tech-reset-${techKey}`}
							onclick={() => onResetTech(techKey)}
						>
							🔒
						</button>
					{/if}
				</span>
			{:else}
				<span></span>
			{/if}
		</div>
	{/each}
</div>

<style>
	.design {
		display: flex;
		flex-direction: column;
		gap: 0.3rem;
	}
	.cols,
	.row {
		display: grid;
		grid-template-columns: 4.5rem 1fr 1fr;
		align-items: center;
		gap: 0.35rem;
	}
	.col-head {
		color: #8890b0;
		font-size: 0.7rem;
		text-align: center;
		text-transform: lowercase;
	}
	.label {
		color: #aab;
		font-size: 0.8rem;
	}
	input {
		font: inherit;
		font-size: 0.8rem;
		width: 100%;
		min-width: 0;
		padding: 0.2rem 0.35rem;
		background: #0a0e1a;
		color: #e8eaf6;
		border: 1px solid #2a3150;
		border-radius: 3px;
		font-variant-numeric: tabular-nums;
	}
	input[data-computed="true"],
	input[readonly] {
		color: #9fb0ff;
		background: #11162a;
	}
	input[aria-invalid="true"] {
		border-color: #d97a7a;
	}
	.tech-cell {
		display: flex;
		align-items: center;
		gap: 0.2rem;
	}
	.lock {
		flex: none;
		padding: 0;
		font-size: 0.7rem;
		line-height: 1;
		background: transparent;
		border: 0;
		cursor: pointer;
	}
</style>
