<!--
Phase 17 ship-classes table. Renders the player's designed ship
classes with sort, filter, double-tap-to-open-designer, and a
per-row Delete action. Source data is the optimistic overlay
(`RenderedReportSource.report.localShipClass`) so a freshly
queued `createShipClass` shows up immediately and a freshly
queued `removeShipClass` disappears immediately — both before the
auto-sync round-trip lands.

The component sits inside the active-view slot owned by
`routes/games/[id]/+layout.svelte`, so it inherits the per-game
`OrderDraftStore` and `RenderedReportSource` through context. No
data fetching is performed here — the layout is responsible.
-->
<script lang="ts">
	import { getContext } from "svelte";

	import type { ShipClassSummary } from "../../api/game-state";
	import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
	import {
		RENDERED_REPORT_CONTEXT_KEY,
		type RenderedReportSource,
	} from "$lib/rendered-report.svelte";
	import {
		ORDER_DRAFT_CONTEXT_KEY,
		OrderDraftStore,
	} from "../../sync/order-draft.svelte";
	import { calculatorLoadRequest } from "$lib/calculator/load-request.svelte";
	import ViewState from "$lib/ui/view-state.svelte";

	type SortColumn =
		| "name"
		| "drive"
		| "armament"
		| "weapons"
		| "shields"
		| "cargo";
	type SortDirection = "asc" | "desc";

	const COLUMN_LABELS: Record<SortColumn, TranslationKey> = {
		name: "game.table.ship_classes.column.name",
		drive: "game.table.ship_classes.column.drive",
		armament: "game.table.ship_classes.column.armament",
		weapons: "game.table.ship_classes.column.weapons",
		shields: "game.table.ship_classes.column.shields",
		cargo: "game.table.ship_classes.column.cargo",
	};

	const COLUMNS: readonly SortColumn[] = [
		"name",
		"drive",
		"armament",
		"weapons",
		"shields",
		"cargo",
	];

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


	let sortColumn: SortColumn = $state("name");
	let sortDirection: SortDirection = $state("asc");
	let filter: string = $state("");

	const localShipClass = $derived<ShipClassSummary[]>(
		rendered?.report?.localShipClass ?? [],
	);
	const reportLoaded = $derived(rendered?.report !== null && rendered?.report !== undefined);

	const filtered = $derived.by(() => {
		const needle = filter.trim().toLowerCase();
		if (needle === "") return localShipClass;
		return localShipClass.filter((cls) =>
			cls.name.toLowerCase().includes(needle),
		);
	});

	const sorted = $derived.by(() => {
		const list = [...filtered];
		const dir = sortDirection === "asc" ? 1 : -1;
		list.sort((a, b) => {
			if (sortColumn === "name") {
				return a.name.localeCompare(b.name) * dir;
			}
			return (a[sortColumn] - b[sortColumn]) * dir;
		});
		return list;
	});

	function toggleSort(column: SortColumn): void {
		if (sortColumn === column) {
			sortDirection = sortDirection === "asc" ? "desc" : "asc";
			return;
		}
		sortColumn = column;
		sortDirection = "asc";
	}

	function ariaSort(column: SortColumn): "ascending" | "descending" | "none" {
		if (sortColumn !== column) return "none";
		return sortDirection === "asc" ? "ascending" : "descending";
	}

	function formatNumber(value: number): string {
		return value.toLocaleString(undefined, { maximumFractionDigits: 2 });
	}

	function openInCalculator(name: string): void {
		calculatorLoadRequest.request(name);
	}

	function newShipClass(): void {
		calculatorLoadRequest.request(null);
	}

	async function deleteShipClass(name: string): Promise<void> {
		if (draft === undefined) return;
		await draft.add({
			kind: "removeShipClass",
			id: crypto.randomUUID(),
			name,
		});
	}
</script>

<section
	class="active-view"
	data-testid="active-view-table"
	data-entity="ship-classes"
>
	<header>
		<h2>{i18n.t("game.table.ship_classes.title")}</h2>
		<div class="controls">
			<input
				type="search"
				class="filter"
				data-testid="ship-classes-filter"
				placeholder={i18n.t("game.table.ship_classes.filter.placeholder")}
				bind:value={filter}
			/>
			<button
				type="button"
				class="new"
				data-testid="ship-classes-new"
				onclick={newShipClass}
			>
				{i18n.t("game.table.ship_classes.action.new")}
			</button>
		</div>
	</header>

	{#if !reportLoaded}
		<ViewState
			kind="loading"
			testid="ship-classes-loading"
			message={i18n.t("game.table.ship_classes.loading")}
		/>
	{:else if localShipClass.length === 0}
		<ViewState
			kind="empty"
			testid="ship-classes-empty"
			message={i18n.t("game.table.ship_classes.empty")}
		/>
	{:else}
		<table class="grid" data-testid="ship-classes-table">
			<thead>
				<tr>
					{#each COLUMNS as column (column)}
						<th aria-sort={ariaSort(column)}>
							<button
								type="button"
								class="sort"
								data-testid="ship-classes-column-{column}"
								onclick={() => toggleSort(column)}
							>
								{i18n.t(COLUMN_LABELS[column])}
								{#if sortColumn === column}
									<span class="sort-indicator" aria-hidden="true">
										{sortDirection === "asc" ? "▲" : "▼"}
									</span>
								{/if}
							</button>
						</th>
					{/each}
					<th>{i18n.t("game.table.ship_classes.column.actions")}</th>
				</tr>
			</thead>
			<tbody>
				{#each sorted as cls (cls.name)}
					<tr
						data-testid="ship-classes-row"
						data-name={cls.name}
						ondblclick={() => openInCalculator(cls.name)}
					>
						<td data-testid="ship-classes-cell-name">{cls.name}</td>
						<td data-testid="ship-classes-cell-drive">{formatNumber(cls.drive)}</td>
						<td data-testid="ship-classes-cell-armament">{cls.armament}</td>
						<td data-testid="ship-classes-cell-weapons">{formatNumber(cls.weapons)}</td>
						<td data-testid="ship-classes-cell-shields">{formatNumber(cls.shields)}</td>
						<td data-testid="ship-classes-cell-cargo">{formatNumber(cls.cargo)}</td>
						<td>
							<button
								type="button"
								class="delete"
								data-testid="ship-classes-delete"
								onclick={() => void deleteShipClass(cls.name)}
							>
								{i18n.t("game.table.ship_classes.action.delete")}
							</button>
						</td>
					</tr>
				{/each}
			</tbody>
		</table>
	{/if}
</section>

<style>
	.active-view {
		padding: 1.5rem;
		font-family: system-ui, sans-serif;
		display: flex;
		flex-direction: column;
		gap: 1rem;
	}
	header {
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}
	header h2 {
		margin: 0;
		font-size: 1.1rem;
	}
	.controls {
		display: flex;
		gap: 0.5rem;
		align-items: center;
		flex-wrap: wrap;
	}
	.filter {
		font: inherit;
		padding: 0.3rem 0.5rem;
		background: var(--color-bg);
		color: var(--color-text);
		border: 1px solid var(--color-border);
		border-radius: 3px;
		flex: 1 1 12rem;
		min-width: 8rem;
	}
	.new {
		font: inherit;
		font-size: 0.9rem;
		padding: 0.3rem 0.7rem;
		background: transparent;
		color: var(--color-text-muted);
		border: 1px solid var(--color-border);
		border-radius: 3px;
		cursor: pointer;
	}
	.new:hover {
		color: var(--color-text);
		border-color: var(--color-accent);
	}
	.grid {
		border-collapse: collapse;
		width: 100%;
		font-variant-numeric: tabular-nums;
	}
	.grid th,
	.grid td {
		padding: 0.4rem 0.6rem;
		text-align: left;
		border-bottom: 1px solid var(--color-border-subtle);
		font-size: 0.9rem;
	}
	.grid th {
		color: var(--color-text-muted);
		text-transform: uppercase;
		letter-spacing: 0.04em;
		font-size: 0.75rem;
	}
	.grid tbody tr {
		cursor: pointer;
	}
	.grid tbody tr:hover {
		background: var(--color-surface);
	}
	.sort {
		font: inherit;
		font-size: inherit;
		text-transform: inherit;
		letter-spacing: inherit;
		color: inherit;
		background: transparent;
		border: none;
		padding: 0;
		cursor: pointer;
		display: inline-flex;
		gap: 0.3rem;
		align-items: baseline;
	}
	.sort-indicator {
		font-size: 0.7em;
	}
	.delete {
		font: inherit;
		font-size: 0.85rem;
		padding: 0.15rem 0.5rem;
		background: transparent;
		color: var(--color-danger);
		border: 1px solid var(--color-border);
		border-radius: 3px;
		cursor: pointer;
	}
	.delete:hover {
		border-color: var(--color-danger);
	}
</style>
