<script lang="ts">
	import {
		Modal,
		Button,
		Label,
		Input,
		Alert,
		Icon,
		Pagination,
		PaginationItem,
		PaginationLink,
		InputGroup,
		Table,
	} from '@sveltestrap/sveltestrap';
	import { TableHandler } from '@vincjo/datatables';
	import { onMount } from 'svelte';
	import { EditableCell, addToast, ConfirmationPrompt } from '$ui';
	import { profilesApi } from '$api';
	import type { RateCurvePoint, SavedFlightProfile } from '$domain';
	import { profilesStore } from './pointsStore';
	import CurveChart from './CurveChart.svelte';

	interface Props {
		isOpen?: boolean;
		onClose?: () => void;
		onSave?: (p: SavedFlightProfile) => void;
		onSelectCurve?: (p: SavedFlightProfile) => void;
		showTable?: boolean;
		curve?: SavedFlightProfile | null;
		editor?: boolean;
		closeOnSave?: boolean;
		closeOnDelete?: boolean;
	}

	let {
		isOpen = $bindable(false),
		onClose = () => {},
		onSave = (_: SavedFlightProfile) => {},
		onSelectCurve = (_: SavedFlightProfile) => {},
		showTable = $bindable(false),
		curve = null,
		editor = false,
		closeOnSave = false,
		closeOnDelete = false,
	}: Props = $props();

	let selectedCurve = $state<SavedFlightProfile | null>(curve);
	let draft = $state<SavedFlightProfile>({ id: 0, name: '', rate_profile_data: [] });
	let newPoint = $state<RateCurvePoint>({ order: 0, time_constraint: 0, alt_constraint: 0, rate: 0 });
	let isEditing = $state(editor);
	let alertText = $state('');
	let isConfirmationVisible = $state(false);

	let table = $derived(new TableHandler($profilesStore, { rowsPerPage: 5 }));
	let search = $derived(table.createSearch(['name']));

	$effect(() => {
		if (editor && curve) {
			selectedCurve = curve;
			draft = { ...curve, rate_profile_data: [...curve.rate_profile_data] };
			isEditing = true;
		}
	});

	function sortByOrder() {
		draft.rate_profile_data = [...draft.rate_profile_data].sort((a, b) => a.order - b.order);
	}

	onMount(async () => {
		if (showTable && $profilesStore.length === 0) {
			try {
				profilesStore.set(await profilesApi.list());
			} catch {
				// ignore; saved profiles endpoint may not be active in dev
			}
		}
	});

	export function openModal(withTable = false) {
		showTable = withTable;
		isOpen = true;
	}

	function close() {
		isOpen = false;
		onClose();
	}

	function handleEdit(c: SavedFlightProfile) {
		selectedCurve = c;
		draft = { ...c, rate_profile_data: [...c.rate_profile_data] };
		isEditing = true;
		showTable = false;
	}

	function confirmDelete(c: SavedFlightProfile) {
		selectedCurve = c;
		isConfirmationVisible = true;
	}

	async function handleDelete() {
		if (!selectedCurve) return;
		try {
			await profilesApi.delete(selectedCurve.id);
			profilesStore.update((items) => items.filter((p) => p.id !== selectedCurve!.id));
			if (closeOnDelete) close();
		} catch (err: unknown) {
			alertText = (err as Error).message;
		}
	}

	async function handleSave() {
		try {
			const saved =
				draft.id && draft.id > 0 ? await profilesApi.update(draft) : await profilesApi.create(draft);
			profilesStore.update((items) => {
				const exists = items.some((p) => p.id === saved.id);
				return exists ? items.map((p) => (p.id === saved.id ? saved : p)) : [...items, saved];
			});
			addToast({ header: 'Curve saved', body: saved.name, color: 'success' });
			if (closeOnSave) close();
			onSave(saved);
		} catch (err: unknown) {
			alertText = (err as Error).message;
		}
	}

	function validatePoint(point: RateCurvePoint): boolean {
		if (point.time_constraint <= 0 && point.time_constraint !== -1) {
			alertText = 'Time constraint invalid';
			return false;
		}
		if (point.alt_constraint < 0 && point.alt_constraint !== -1) {
			alertText = 'Altitude constraint invalid';
			return false;
		}
		if (point.alt_constraint === -1 && point.time_constraint === -1) {
			alertText = 'At least one constraint required';
			return false;
		}
		return true;
	}

	function addPoint() {
		if (!validatePoint(newPoint)) return;
		const maxOrder = draft.rate_profile_data.reduce((m, p) => Math.max(m, p.order), -1);
		draft.rate_profile_data = [...draft.rate_profile_data, { ...newPoint, order: maxOrder + 1 }];
		newPoint = { order: 0, time_constraint: 0, alt_constraint: 0, rate: 0 };
		alertText = '';
	}

	function removePoint(index: number) {
		draft.rate_profile_data.splice(index, 1);
		draft.rate_profile_data.forEach((p, i) => (p.order = i));
		draft.rate_profile_data = [...draft.rate_profile_data];
	}

	function movePoint(index: number, direction: number) {
		const target = index + direction;
		if (target < 0 || target >= draft.rate_profile_data.length) return;
		const t = draft.rate_profile_data[index].order;
		draft.rate_profile_data[index].order = draft.rate_profile_data[target].order;
		draft.rate_profile_data[target].order = t;
		sortByOrder();
	}
</script>

<Modal
	{isOpen}
	toggle={close}
	size="xl"
	fade={false}
	scrollable
	class={isConfirmationVisible ? 'modal-tinted' : ''}>
	<div class="modal-header">
		<h5 class="modal-title">{showTable ? 'Curves' : isEditing ? 'Edit Curve' : 'New Curve'}</h5>
		<Button close onclick={close} />
	</div>
	<div class="modal-body">
		{#if showTable}
			<InputGroup class="mb-2">
				<Input
					type="text"
					placeholder="Search..."
					bind:value={search.value}
					oninput={() => search.set()} />
				<Button
					onclick={() => {
						search.value = '';
						search.set();
					}}>
					<Icon name="x" />
				</Button>
			</InputGroup>
			<div bind:this={table.element} class="table-responsive">
				<Table class="table-sm mb-0">
					<thead>
						<tr><th style="width: 70%;">Name</th><th>Actions</th></tr>
					</thead>
					<tbody>
						{#each table.rows as c (c.id)}
							<tr>
								<td>{c.name}</td>
								<td>
									<Button size="sm" color="primary" onclick={() => onSelectCurve(c)}>
										<Icon name="check-lg" />
									</Button>
									<Button size="sm" color="secondary" onclick={() => handleEdit(c)} class="ms-1">
										<Icon name="pencil" />
									</Button>
									<Button size="sm" color="danger" onclick={() => confirmDelete(c)} class="ms-1">
										<Icon name="trash" />
									</Button>
								</td>
							</tr>
						{/each}
					</tbody>
				</Table>
			</div>
			<Pagination size="sm">
				<PaginationItem>
					<PaginationLink previous onclick={() => table.setPage('previous')} />
				</PaginationItem>
				{#each table.pagesWithEllipsis as page}
					<PaginationItem active={table.currentPage === page}>
						<PaginationLink onclick={() => table.setPage(page)}>{page}</PaginationLink>
					</PaginationItem>
				{/each}
				<PaginationItem>
					<PaginationLink next onclick={() => table.setPage('next')} />
				</PaginationItem>
			</Pagination>
		{:else}
			<div class="row">
				<div class="col-lg-6">
					<div class="mb-2">
						<Label class="small">Curve name</Label>
						<Input class="form-control-sm" type="text" bind:value={draft.name} required />
					</div>
					<h6>Points</h6>
					<Alert color="danger" isOpen={!!alertText} toggle={() => (alertText = '')} fade={false} class="mb-2">
						<Icon name="exclamation-triangle" class="me-2" />
						{alertText}
					</Alert>
					<div class="table-responsive small" style="max-height: 300px;">
						<table class="table table-sm border mb-0">
							<thead>
								<tr>
									<th></th>
									<th>t, sec</th>
									<th>alt, m</th>
									<th>rate, m/s</th>
									<th></th>
								</tr>
							</thead>
							<tbody>
								{#each draft.rate_profile_data as point, i (point.order)}
									{@const isFirst = i === 0}
									{@const isLast = i === draft.rate_profile_data.length - 1}
									<tr>
										<td>
											<Button
												size="sm"
												class="p-0 border-0 bg-transparent"
												onclick={() => movePoint(i, -1)}
												disabled={isFirst}>
												<Icon name="chevron-up" />
											</Button>
											<Button
												size="sm"
												class="p-0 border-0 bg-transparent"
												onclick={() => movePoint(i, 1)}
												disabled={isLast}>
												<Icon name="chevron-down" />
											</Button>
										</td>
										<EditableCell
											bind:value={point.time_constraint}
											onchange={() => (draft.rate_profile_data = [...draft.rate_profile_data])}
											valueSuffix=" s"
											emptyValue={-1} />
										<EditableCell
											bind:value={point.alt_constraint}
											onchange={() => (draft.rate_profile_data = [...draft.rate_profile_data])}
											valueSuffix=" m"
											emptyValue={-1} />
										<EditableCell
											bind:value={point.rate}
											onchange={() => (draft.rate_profile_data = [...draft.rate_profile_data])}
											valueSuffix=" m/s" />
										<td>
											<Button size="sm" color="danger" onclick={() => removePoint(i)} class="p-0 border-0 bg-transparent">
												<Icon name="trash" />
											</Button>
										</td>
									</tr>
								{:else}
									<tr><td colspan="5" class="text-center text-muted">No points yet</td></tr>
								{/each}
							</tbody>
							<tfoot>
								<tr>
									<td></td>
									<td><Input class="form-control-sm" type="number" placeholder="t" bind:value={newPoint.time_constraint} /></td>
									<td><Input class="form-control-sm" type="number" placeholder="alt" bind:value={newPoint.alt_constraint} /></td>
									<td><Input class="form-control-sm" type="number" placeholder="rate" bind:value={newPoint.rate} /></td>
									<td>
										<Button size="sm" color="success" onclick={addPoint} class="p-0 border-0 bg-transparent">Add</Button>
									</td>
								</tr>
							</tfoot>
						</table>
					</div>
				</div>
				<div class="col-lg-6">
					<CurveChart curve={draft} onUpdate={(pts) => (draft.rate_profile_data = pts)} />
				</div>
			</div>
			<hr />
			<div class="d-grid gap-2 d-md-flex justify-content-end">
				<Button color="success" size="sm" onclick={handleSave}>
					{isEditing ? 'Update' : 'Save'}
				</Button>
			</div>
		{/if}
	</div>
</Modal>

<ConfirmationPrompt
	bind:isOpen={isConfirmationVisible}
	title="Confirm deletion"
	confirmText="Delete"
	cancelText="Cancel"
	confirmVariant="danger"
	onconfirm={handleDelete}
	oncancel={() => (isConfirmationVisible = false)}>
	<p>Delete curve "{selectedCurve?.name}"?</p>
</ConfirmationPrompt>
