<script lang="ts">
	import { onMount } from 'svelte';
	import PageHeader from '$lib/components/layout/PageHeader.svelte';
	import LoadingState from '$lib/components/common/LoadingState.svelte';
	import FilterBar from '$lib/components/common/FilterBar.svelte';
	import { AlertCircle, ChevronLeft, ChevronRight } from 'lucide-svelte';
	import {
		projectMonthPlanService,
		type ProjectMonthPlan,
		type BulkUpdateRequest
	} from '$lib/services/projectMonthPlanService';

	let plans = $state<ProjectMonthPlan[]>([]);
	let loading = $state(true);
	let error = $state<string | null>(null);
	let isSavingCell = $state(false);
	let savingCellKey = $state<{ projectId: string; month: string } | null>(null);
	let saveSuccess = $state<{ projectId: string; month: string } | null>(null);
	let cellError = $state<{ projectId: string; month: string; message: string } | null>(null);

	let currentYear = $state(new Date().getFullYear());
	let codeFilter = $state('');
	let projectFilter = $state('');
	let statusFilter = $state('all');
	let sortBy = $state('default');

	let editingCell = $state<{ projectId: string; month: string } | null>(null);
	let editValue = $state<number | null>(null);
	let originalValue = $state<number | null>(null);
	let moveToNext = $state(false);

	function autofocus(node: HTMLInputElement) {
		node.focus();
		node.select();
	}

	onMount(() => {
		loadPlans();
	});

	async function loadPlans() {
		try {
			loading = true;
			error = null;
			const response = await projectMonthPlanService.getPlans(currentYear);
			plans = response || [];
		} catch (err) {
			error = err instanceof Error ? err.message : 'Failed to load plans';
			console.error('Error loading plans:', err);
		} finally {
			loading = false;
		}
	}

	function getMonthColumns(): string[] {
		const months = [];
		for (let m = 1; m <= 12; m++) {
			months.push(`${currentYear}-${String(m).padStart(2, '0')}-01`);
		}
		return months;
	}

	function toNumber(value: unknown): number {
		const parsed = Number(value);
		return Number.isFinite(parsed) ? parsed : 0;
	}

	function getPlanValue(plan: ProjectMonthPlan, month: string): number | null {
		const monthData = plan.months[month];
		if (!monthData) return null;
		if (monthData.is_blank) return null;
		return toNumber(monthData.planned_hours);
	}

	function getReconciliationBadgeClass(status: string | undefined): string {
		switch (status) {
			case 'OVER':
				return 'badge-error';
			case 'UNDER':
				return 'badge-warning';
			case 'MATCH':
				return 'badge-neutral';
			default:
				return 'badge-ghost';
		}
	}

	function getStatusBadgeClass(status: string | null | undefined): string {
		switch (status) {
			case 'Estimate Approved':
				return 'badge-success';
			case 'Resource Allocation':
			case 'Sprint 0':
				return 'badge-info';
			case 'In Progress':
				return 'badge-primary';
			case 'UAT':
				return 'badge-secondary';
			case 'Handover / Sign-off':
				return 'badge-accent';
			case 'Closed':
				return 'badge-neutral';
			case 'On Hold':
				return 'badge-warning';
			case 'Cancelled':
				return 'badge-error';
			default:
				return 'badge-ghost';
		}
	}

	function startEdit(projectId: string, month: string, currentValue: number | null) {
		if (isSavingCell) return;
		editingCell = { projectId, month };
		editValue = currentValue;
		originalValue = currentValue;
		cellError = null;
	}

	function moveToNextCell(currentProjectId: string, currentMonth: string) {
		const months = getMonthColumns();
		const currentIndex = months.indexOf(currentMonth);
		if (currentIndex < months.length - 1) {
			const nextMonth = months[currentIndex + 1];
			const plan = plans.find((p) => p.project_id === currentProjectId);
			if (plan) {
				startEdit(currentProjectId, nextMonth, getPlanValue(plan, nextMonth));
			}
		}
	}

	async function saveCell() {
		if (!editingCell || isSavingCell) return;

		const snapshot = {
			projectId: editingCell.projectId,
			month: editingCell.month,
			monthStr: editingCell.month.substring(0, 7),
			valueToSave: editValue,
			shouldMoveNext: moveToNext
		};

		if (snapshot.valueToSave === originalValue) {
			editingCell = null;
			editValue = null;
			originalValue = null;
			moveToNext = false;
			return;
		}

		try {
			isSavingCell = true;
			savingCellKey = { projectId: snapshot.projectId, month: snapshot.month };
			cellError = null;

			const request: BulkUpdateRequest = {
				year: currentYear,
				items: [
					{
						project_id: snapshot.projectId,
						month: snapshot.monthStr,
						planned_hours: snapshot.valueToSave
					}
				]
			};

			await projectMonthPlanService.bulkUpdate(request);

			editingCell = null;
			editValue = null;
			originalValue = null;
			moveToNext = false;

			await loadPlans();

			saveSuccess = { projectId: snapshot.projectId, month: snapshot.month };
			setTimeout(() => {
				saveSuccess = null;
			}, 1500);

			if (snapshot.shouldMoveNext) {
				moveToNextCell(snapshot.projectId, snapshot.month);
			}
		} catch (err) {
			const message = err instanceof Error ? err.message : 'Failed to save';
			cellError = { projectId: snapshot.projectId, month: snapshot.month, message };
			console.error('Error saving plan:', err);
			editingCell = null;
			editValue = null;
			originalValue = null;
			moveToNext = false;
		} finally {
			isSavingCell = false;
			savingCellKey = null;
		}
	}

	function cancelEdit() {
		editingCell = null;
		editValue = null;
		originalValue = null;
		moveToNext = false;
		cellError = null;
	}

	function previousYear() {
		currentYear--;
		loadPlans();
	}

	function nextYear() {
		currentYear++;
		loadPlans();
	}

	function formatMonth(monthStr: string): string {
		const [, month] = monthStr.split('-');
		return new Date(2000, parseInt(month) - 1).toLocaleDateString('en-US', { month: 'short' });
	}

	function isCellEditing(projectId: string, month: string): boolean {
		return editingCell?.projectId === projectId && editingCell?.month === month;
	}

	function hasCellError(projectId: string, month: string): boolean {
		return cellError?.projectId === projectId && cellError?.month === month;
	}

	function getStatusLabel(status: string | null | undefined): string {
		if (!status) {
			return '-';
		}

		const statusMap: Record<string, string> = {
			'Estimate Approved': 'Est. Approved',
			'Resource Allocation': 'Resourcing',
			'Handover / Sign-off': 'Handover',
			'In Progress': 'In Progress',
			'On Hold': 'On Hold',
			Cancelled: 'Cancelled',
			Closed: 'Closed',
			UAT: 'UAT',
			'Sprint 0': 'Sprint 0',
			Estimation: 'Estimation',
			'Pre-sales': 'Pre-sales',
			'SOW Approval': 'SOW Approval',
			'Estimate Rework': 'Est. Rework',
			'Project Kickoff': 'Kickoff'
		};

		return statusMap[status] ?? status;
	}

	function getMonthlyTotals(): Record<string, number> {
		const totals: Record<string, number> = {};
		const displayedPlans = getDisplayedPlans();
		for (const month of getMonthColumns()) {
			totals[month] = displayedPlans.reduce((sum, plan) => {
				const value = getPlanValue(plan, month);
				return sum + (value ?? 0);
			}, 0);
		}

		return totals;
	}

	function getStatusOptions(): string[] {
		const uniqueStatuses = new Set(
			plans
				.map((plan) => plan.project_status)
				.filter((status): status is string => Boolean(status))
		);

		return Array.from(uniqueStatuses).sort((a, b) => a.localeCompare(b));
	}

	function getDisplayedPlans(): ProjectMonthPlan[] {
		const normalizedCode = codeFilter.trim().toLowerCase();
		const normalizedProject = projectFilter.trim().toLowerCase();

		const filtered = plans.filter((plan) => {
			const matchesCode =
				normalizedCode === '' || plan.project_code.toLowerCase().includes(normalizedCode);
			const matchesProject =
				normalizedProject === '' || plan.project_name.toLowerCase().includes(normalizedProject);
			const matchesStatus =
				statusFilter === 'all' || (plan.project_status ?? '') === statusFilter;

			return matchesCode && matchesProject && matchesStatus;
		});

		return filtered.sort((a, b) => {
			switch (sortBy) {
				case 'code-asc':
					return a.project_code.localeCompare(b.project_code);
				case 'code-desc':
					return b.project_code.localeCompare(a.project_code);
				case 'project-asc':
					return a.project_name.localeCompare(b.project_name);
				case 'project-desc':
					return b.project_name.localeCompare(a.project_name);
			case 'sum-asc':
				return toNumber(a.plan_sum) - toNumber(b.plan_sum);
			case 'sum-desc':
				return toNumber(b.plan_sum) - toNumber(a.plan_sum);
				default:
					return 0;
			}
		});
	}

	function clearFilters() {
		codeFilter = '';
		projectFilter = '';
		statusFilter = 'all';
		sortBy = 'default';
	}

</script>

<svelte:head>
	<title>Planning | Headroom</title>
</svelte:head>

<PageHeader title="Project-Month Planning" description="Set explicit monthly planned effort per project">
	{#snippet children()}
		<div class="flex items-center gap-2">
			<button class="btn btn-ghost btn-sm btn-circle" onclick={previousYear}>
				<ChevronLeft size={18} />
			</button>
			<span class="min-w-[80px] text-center font-medium">
				{currentYear}
			</span>
			<button class="btn btn-ghost btn-sm btn-circle" onclick={nextYear}>
				<ChevronRight size={18} />
			</button>
		</div>
	{/snippet}
</PageHeader>

{#if loading}
	<LoadingState />
{:else if error}
	<div class="alert alert-error">
		<AlertCircle size={20} />
		<span>{error}</span>
	</div>
{:else}
	<FilterBar
		searchValue={codeFilter}
		searchPlaceholder="Filter by project code..."
		onSearchChange={(value) => {
			codeFilter = value;
		}}
		onClear={clearFilters}
	>
		{#snippet children()}
			<input
				type="text"
				placeholder="Filter by project name..."
				class="input input-bordered input-sm w-56"
				bind:value={projectFilter}
			/>
			<select class="select select-bordered select-sm w-48" bind:value={statusFilter}>
				<option value="all">All statuses</option>
				{#each getStatusOptions() as status}
					<option value={status}>{status}</option>
				{/each}
			</select>
			<select class="select select-bordered select-sm w-48" bind:value={sortBy}>
				<option value="default">Default order</option>
				<option value="code-asc">Code A-Z</option>
				<option value="code-desc">Code Z-A</option>
				<option value="project-asc">Project A-Z</option>
				<option value="project-desc">Project Z-A</option>
				<option value="sum-desc">Plan Sum high-low</option>
				<option value="sum-asc">Plan Sum low-high</option>
			</select>
		{/snippet}
	</FilterBar>

	<div class="overflow-x-auto planning-grid">
		<table class="table table-xs w-full min-w-[1400px]">
			<thead>
				<tr>
					<th class="sticky left-0 bg-base-200 z-30 min-w-[80px]">Code</th>
					<th class="sticky left-[80px] bg-base-200 z-30 min-w-[220px] shadow-[4px_0_8px_-4px_oklch(0_0_0/0.12)]">Project</th>
					<th class="text-center min-w-[110px] bg-base-200">Status</th>
					<th class="text-center min-w-[60px] bg-base-200">Approved</th>
					{#each getMonthColumns() as column}
						<th class="text-center min-w-[80px]">{formatMonth(column)}</th>
					{/each}
					<th class="text-center min-w-[80px] bg-base-200 font-bold">Plan Sum</th>
					<th class="text-center min-w-[80px] bg-base-200 font-bold">Recon</th>
				</tr>
			</thead>
		<tbody>
		{#each getDisplayedPlans() as plan}
			{@const planSum = toNumber(plan.plan_sum)}
			<tr class="hover">
				<td class="sticky left-0 bg-base-100 z-20 font-mono text-xs">
					{plan.project_code}
				</td>
				<td class="sticky left-[80px] bg-base-100 z-20 font-medium max-w-[220px] truncate shadow-[4px_0_8px_-4px_oklch(0_0_0/0.08)]" title={plan.project_name}>
					{plan.project_name}
				</td>
				<td class="text-center">
					<span class="badge badge-sm whitespace-nowrap {getStatusBadgeClass(plan.project_status)}" title={plan.project_status || ''}>
						{getStatusLabel(plan.project_status)}
					</span>
				</td>
				<td class="text-center bg-base-200/50">
					{plan.approved_estimate ? `${plan.approved_estimate}h` : '-'}
				</td>
			{#each getMonthColumns() as month}
				<td
					class="text-center cursor-pointer hover:bg-base-200 transition-colors relative {isCellEditing(plan.project_id, month) ? 'ring-2 ring-primary bg-primary/5' : ''}"
					onclick={() => !isCellEditing(plan.project_id, month) && startEdit(plan.project_id, month, getPlanValue(plan, month))}
				>
					{#if isCellEditing(plan.project_id, month)}
						<input
							type="number"
							class="input input-xs input-bordered w-16 text-center"
							bind:value={editValue}
							use:autofocus
							onkeydown={async (e) => {
								if (e.key === 'Enter') {
									e.preventDefault();
									moveToNext = true;
									await saveCell();
								}
								if (e.key === 'Escape') {
									e.preventDefault();
									cancelEdit();
								}
							}}
							disabled={isSavingCell}
						/>
					{:else if hasCellError(plan.project_id, month)}
						<span class="text-error cursor-pointer" title={cellError?.message || 'Error'}>
							{getPlanValue(plan, month) ?? '-'}h ✗
						</span>
					{:else if isSavingCell && savingCellKey?.projectId === plan.project_id && savingCellKey?.month === month}
						<span class="loading loading-xs loading-spinner text-primary"></span>
					{:else if saveSuccess?.projectId === plan.project_id && saveSuccess?.month === month}
						<span class="text-success">{getPlanValue(plan, month) ?? 0}h ✓</span>
					{:else}
						{@const value = getPlanValue(plan, month)}
						{#if value !== null}
							<span>{value}h</span>
						{:else}
							<span class="text-base-content/30">-</span>
						{/if}
					{/if}
				</td>
			{/each}
			<td class="text-center bg-base-200/50 font-medium">
				{planSum > 0 ? `${planSum}h` : '0h'}
			</td>
			<td class="text-center bg-base-200">
				{#if plan.approved_estimate && plan.approved_estimate > 0}
					<span class="badge badge-sm {getReconciliationBadgeClass(plan.reconciliation_status)}">
						{plan.reconciliation_status || '-'}
								</span>
							{:else}
								<span>-</span>
							{/if}
						</td>
					</tr>
				{/each}
			</tbody>
			{#if getDisplayedPlans().length > 0}
				{@const monthlyTotals = getMonthlyTotals()}
				{@const grandTotal = Object.values(monthlyTotals).reduce((sum, value) => sum + toNumber(value), 0)}
				<tfoot>
					<tr class="bg-base-200 border-t-2 border-base-300 font-semibold">
						<td class="sticky left-0 bg-base-200 z-30"></td>
						<td class="sticky left-[80px] bg-base-200 z-30 shadow-[4px_0_8px_-4px_oklch(0_0_0/0.12)]">Monthly Totals</td>
						<td></td>
						<td></td>
						{#each getMonthColumns() as month}
							{@const monthlyTotal = monthlyTotals[month]}
							<td class="text-center tabular-nums">
								{#if monthlyTotal > 0}
									<span class="font-bold text-primary">{monthlyTotal}h</span>
								{:else}
									<span class="text-base-content/30">0h</span>
								{/if}
							</td>
						{/each}
					<td class="text-center bg-base-300/50 tabular-nums">
						<span class="font-bold">{grandTotal}h</span>
						</td>
						<td></td>
					</tr>
				</tfoot>
			{:else}
				<tfoot>
					<tr>
						<td colspan="18" class="text-center py-8">
							<span class="text-base-content/50">No projects match the current filters.</span>
						</td>
					</tr>
				</tfoot>
			{/if}
		</table>
	</div>
{/if}
