<script lang="ts">
	import { onMount } from 'svelte';
	import { page } from '$app/stores';
	import { get } from 'svelte/store';
	import { addNotification } from '$lib/core/stores/noti.js';
	import { departmentStore } from '$lib/core/stores/departments.js';
	import {
		findHeaderIndex,
		PRICE_HEADER_NAMES_BY_COUNTRY,
		sheetPriceAllRows,
		sheetPriceHeader,
		sheetPriceLoading,
		type GristCell
	} from '$lib/core/stores/sheetStore.js';
	import {
		addSheetPrice,
		requestAllSheetPrice,
		updateSheetPrice
	} from '$lib/core/services/sheetService.js';
	import { waitForOpenSocket } from '$lib/core/stores/websocketStore.js';
	import { referenceFromPage } from '$lib/core/stores/recipeStore.js';
	import Button from '$lib/components/ui/button/button.svelte';
	import Input from '$lib/components/ui/input/input.svelte';
	import * as Table from '$lib/components/ui/table/index.js';
	import Spinner from '$lib/components/ui/spinner/spinner.svelte';
	import { Plus, RefreshCw, Save } from '@lucide/svelte/icons';

	let selectedCountry = $state<string>($page.params.country || get(departmentStore) || '');
	let search = $state('');
	let saving = $state(false);
	let newProductCode = $state('');
	let newName = $state('');
	let newPrice = $state('');
	let priceEdits = $state<Record<string, string>>({});
	let selectedCountryKey = $derived(selectedCountry.toLowerCase());
	let loading = $derived($sheetPriceLoading);
	let header = $derived($sheetPriceHeader[selectedCountryKey] ?? []);
	let priceColumnIndex = $derived(getPriceColumnIndex());
	let rowsByProductCode = $derived($sheetPriceAllRows[selectedCountryKey] ?? {});
	let rows = $derived(
		Object.values(rowsByProductCode)
			.flat()
			.sort((a, b) => a.row - b.row)
	);
	let filteredRows = $derived(
		rows.filter((row) => {
			const keyword = search.trim().toLowerCase();
			if (!keyword) return true;
			return row.cells.some((cell) =>
				String(cell.value ?? '')
					.toLowerCase()
					.includes(keyword)
			);
		})
	);
	let visibleHeader = $derived(
		Array.from(
			{ length: Math.max(5, Math.min(12, header.length || 5)) },
			(_, index) => header[index] || String.fromCharCode(65 + index)
		)
	);

	onMount(() => {
		referenceFromPage.set('price');
		if (selectedCountry) departmentStore.set(selectedCountry);
		void loadPrice();
	});

	async function loadPrice() {
		if (!selectedCountry) return;

		const socket = await waitForOpenSocket();
		if (!socket) {
			addNotification('ERR:WebSocket not connected');
			return;
		}

		const sent = await requestAllSheetPrice(selectedCountry, true);
		if (!sent) addNotification('ERR:Failed to request Price data');
	}

	function getPriceColumnIndex() {
		const headerNames =
			PRICE_HEADER_NAMES_BY_COUNTRY[selectedCountryKey] || PRICE_HEADER_NAMES_BY_COUNTRY.default;
		const col = findHeaderIndex(header, headerNames.cash_price);
		return col > 0 ? col : 5;
	}

	function getCellValue(cells: GristCell[], column: number) {
		return String(cells.find((cell) => cell.coord?.col === column)?.value ?? '');
	}

	function getEditKey(row: { row: number }) {
		return String(row.row);
	}

	function getEditedPrice(row: { row: number; cells: GristCell[] }) {
		const key = getEditKey(row);
		return priceEdits[key] ?? getCellValue(row.cells, priceColumnIndex);
	}

	function setEditedPrice(row: { row: number }, value: string) {
		priceEdits = { ...priceEdits, [getEditKey(row)]: value };
	}

	function getChangedUpdates() {
		return rows
			.map((row) => {
				const key = getEditKey(row);
				if (!(key in priceEdits)) return null;

				const original = getCellValue(row.cells, priceColumnIndex);
				const value = priceEdits[key];
				if (value === original) return null;

				return {
					row_index: row.row,
					cells: [{ value, coord: { row: row.row, col: priceColumnIndex } }]
				};
			})
			.filter((update): update is NonNullable<typeof update> => update !== null);
	}

	async function savePriceChanges() {
		const updates = getChangedUpdates();
		if (updates.length === 0) {
			addNotification('INFO:No price changes to save');
			return;
		}

		saving = true;
		try {
			const sent = await updateSheetPrice(selectedCountry, updates);
			if (!sent) {
				addNotification('ERR:Failed to send price updates');
				return;
			}
			addNotification(`INFO:Updated ${updates.length} price row(s)`);
			priceEdits = {};
			await loadPrice();
		} finally {
			saving = false;
		}
	}

	async function addPriceRow() {
		const productCode = String(newProductCode).trim();
		const name = String(newName).trim();
		const price = String(newPrice).trim();

		if (!productCode) {
			addNotification('WARN:ProductCode is required');
			return;
		}
		if (!price) {
			addNotification('WARN:Price is required');
			return;
		}

		const cells = Array.from({ length: Math.max(header.length, priceColumnIndex) }, () => '');
		cells[0] = productCode;
		cells[1] = name;
		cells[priceColumnIndex - 1] = price;

		saving = true;
		try {
			const sent = await addSheetPrice(selectedCountry, [{ cells }]);
			if (!sent) {
				addNotification('ERR:Failed to add price row');
				return;
			}
			addNotification(`INFO:Added price row ${productCode}`);
			newProductCode = '';
			newName = '';
			newPrice = '';
			await loadPrice();
		} finally {
			saving = false;
		}
	}
</script>

<div class="min-h-screen bg-background">
	<div class="w-full px-6 py-8 lg:px-8">
		<div class="mb-7 flex flex-wrap items-start justify-between gap-5">
			<div>
				<h1 class="text-4xl leading-tight font-bold tracking-normal">
					Price [ {selectedCountry.toUpperCase()} ]
				</h1>
				<p class="mt-4 text-muted-foreground">View main Price sheet data for this country.</p>
			</div>
			<div class="flex flex-wrap items-center gap-3">
				<Button onclick={savePriceChanges} disabled={saving || getChangedUpdates().length === 0}>
					{#if saving}
						<Spinner class="mr-2 h-4 w-4" />
						Saving
					{:else}
						<Save class="mr-2 h-4 w-4" />
						Save Changes ({getChangedUpdates().length})
					{/if}
				</Button>
				<Button variant="outline" onclick={loadPrice} disabled={loading || saving}>
					{#if loading}
						<Spinner class="mr-2 h-4 w-4" />
						Loading
					{:else}
						<RefreshCw class="mr-2 h-4 w-4" />
						Refresh
					{/if}
				</Button>
			</div>
		</div>

		<div
			class="mb-5 grid gap-3 rounded-xl border bg-card/60 p-4 lg:grid-cols-[1fr_180px_180px_140px_auto]"
		>
			<Input placeholder="Search product code, name, or price..." bind:value={search} />
			<Input placeholder="New ProductCode" bind:value={newProductCode} class="font-mono" />
			<Input placeholder="Name" bind:value={newName} />
			<Input placeholder="Price" bind:value={newPrice} type="number" min="0" step="0.01" />
			<Button variant="outline" onclick={addPriceRow} disabled={saving}>
				<Plus class="mr-2 h-4 w-4" />
				Add Row
			</Button>
		</div>

		<div class="rounded-xl border bg-card/60">
			{#if loading && rows.length === 0}
				<div class="flex h-64 items-center justify-center text-muted-foreground">
					<Spinner class="mr-3 h-6 w-6" />
					Loading Price data...
				</div>
			{:else if rows.length === 0}
				<div class="flex h-64 items-center justify-center text-muted-foreground">
					No Price data loaded. Click Refresh to load data.
				</div>
			{:else if filteredRows.length === 0}
				<div class="flex h-64 items-center justify-center text-muted-foreground">
					No rows match your search.
				</div>
			{:else}
				<div class="max-h-[calc(100vh-220px)] overflow-auto">
					<Table.Root>
						<Table.Header class="sticky top-0 z-10 bg-card">
							<Table.Row>
								<Table.Head class="w-20">Row</Table.Head>
								{#each visibleHeader as column}
									<Table.Head>{column}</Table.Head>
								{/each}
							</Table.Row>
						</Table.Header>
						<Table.Body>
							{#each filteredRows as row, index (`${row.row}-${row.cells[0]?.value ?? ''}-${index}`)}
								<Table.Row>
									<Table.Cell class="font-mono text-xs text-muted-foreground">{row.row}</Table.Cell>
									{#each visibleHeader as _, index}
										<Table.Cell class={index === 0 ? 'font-mono text-sm' : 'text-sm'}>
											{#if index + 1 === priceColumnIndex}
												<Input
													type="number"
													min="0"
													step="0.01"
													class="h-8 w-28 text-right font-semibold"
													value={getEditedPrice(row)}
													oninput={(event) => setEditedPrice(row, event.currentTarget.value)}
												/>
											{:else}
												{row.cells.find((cell) => cell.coord?.col === index + 1)?.value ?? ''}
											{/if}
										</Table.Cell>
									{/each}
								</Table.Row>
							{/each}
						</Table.Body>
					</Table.Root>
				</div>
			{/if}
		</div>
	</div>
</div>
