<script lang="ts">
	import { fade, scale } from 'svelte/transition';
	import { client } from '$lib/graphql/client';
	import {
		ACCOUNTS_WITH_SCHEDULES_QUERY,
		type AccountForGeneration,
		type AccountsWithSchedulesQueryResult,
		type ScheduleForGeneration,
		type AddressForGeneration
	} from '$lib/graphql/queries/accounts';
	import {
		GENERATE_SERVICES_BY_MONTH,
		type GenerateServicesInput,
		type GenerateServicesByMonthResult
	} from '$lib/graphql/mutations/service';

	interface Props {
		open: boolean;
		onClose: () => void;
		onSuccess?: () => void;
	}

	let { open, onClose, onSuccess }: Props = $props();

	// State
	let loading = $state(true);
	let generating = $state(false);
	let error = $state<string | null>(null);
	let accounts = $state<AccountForGeneration[]>([]);

	// Month/Year selection (default to next month)
	const now = new Date();
	const defaultMonth = now.getMonth() === 11 ? 1 : now.getMonth() + 2;
	const defaultYear = now.getMonth() === 11 ? now.getFullYear() + 1 : now.getFullYear();
	let selectedMonth = $state(defaultMonth);
	let selectedYear = $state(defaultYear);

	// Schedule selection: Map of scheduleId -> { accountAddressId, schedule }
	interface ScheduleSelection {
		accountAddressId: string;
		schedule: ScheduleForGeneration;
		accountName: string;
		addressName: string;
	}
	let selectedSchedules = $state<Map<string, ScheduleSelection>>(new Map());

	// Expanded accounts for accordion
	let expandedAccounts = $state<Set<string>>(new Set());

	// Results
	let results = $state<{ success: number; errors: string[] } | null>(null);

	// Months for dropdown
	const months = [
		{ value: 1, label: 'January' },
		{ value: 2, label: 'February' },
		{ value: 3, label: 'March' },
		{ value: 4, label: 'April' },
		{ value: 5, label: 'May' },
		{ value: 6, label: 'June' },
		{ value: 7, label: 'July' },
		{ value: 8, label: 'August' },
		{ value: 9, label: 'September' },
		{ value: 10, label: 'October' },
		{ value: 11, label: 'November' },
		{ value: 12, label: 'December' }
	];

	// Years for dropdown (current year - 1 to current year + 2)
	const currentYear = now.getFullYear();
	const years = [currentYear - 1, currentYear, currentYear + 1, currentYear + 2];

	// Load accounts when modal opens
	$effect(() => {
		if (open) {
			loadAccounts();
		} else {
			// Reset state when modal closes
			selectedSchedules = new Map();
			expandedAccounts = new Set();
			results = null;
			error = null;
		}
	});

	async function loadAccounts() {
		loading = true;
		error = null;

		try {
			const result = await client.query<AccountsWithSchedulesQueryResult>({
				query: ACCOUNTS_WITH_SCHEDULES_QUERY,
				fetchPolicy: 'network-only'
			});

			if (result.data?.accounts) {
				// Filter to only accounts with addresses that have schedules
				accounts = result.data.accounts.filter((a: AccountForGeneration) =>
					a.addresses.some(
						(addr: AddressForGeneration) => addr.isActive && addr.schedules.length > 0
					)
				);
			}
		} catch (e) {
			error = e instanceof Error ? e.message : 'Failed to load accounts';
		} finally {
			loading = false;
		}
	}

	function toggleAccount(accountId: string) {
		const newExpanded = new Set(expandedAccounts);
		if (newExpanded.has(accountId)) {
			newExpanded.delete(accountId);
		} else {
			newExpanded.add(accountId);
		}
		expandedAccounts = newExpanded;
	}

	function toggleSchedule(
		schedule: ScheduleForGeneration,
		address: AddressForGeneration,
		account: AccountForGeneration
	) {
		const newSelected = new Map(selectedSchedules);
		if (newSelected.has(schedule.id)) {
			newSelected.delete(schedule.id);
		} else {
			newSelected.set(schedule.id, {
				accountAddressId: address.id,
				schedule,
				accountName: account.name,
				addressName: address.name || `${address.city}, ${address.state}`
			});
		}
		selectedSchedules = newSelected;
	}

	function selectAll() {
		const newSelected = new Map<string, ScheduleSelection>();
		for (const account of accounts) {
			for (const address of account.addresses) {
				if (!address.isActive) continue;
				for (const schedule of address.schedules) {
					if (isScheduleActive(schedule)) {
						newSelected.set(schedule.id, {
							accountAddressId: address.id,
							schedule,
							accountName: account.name,
							addressName: address.name || `${address.city}, ${address.state}`
						});
					}
				}
			}
		}
		selectedSchedules = newSelected;
	}

	function clearAll() {
		selectedSchedules = new Map();
	}

	function isScheduleActive(schedule: ScheduleForGeneration): boolean {
		const today = new Date().toISOString().split('T')[0];
		if (schedule.endDate && schedule.endDate < today) return false;
		return true;
	}

	function getDaysInMonth(month: number, year: number): number {
		return new Date(year, month, 0).getDate();
	}

	function getWeekday(year: number, month: number, day: number): number {
		// Returns 0=Monday, 6=Sunday (chrono convention)
		const date = new Date(year, month - 1, day);
		const jsWeekday = date.getDay(); // 0=Sunday, 6=Saturday
		return jsWeekday === 0 ? 6 : jsWeekday - 1;
	}

	// Calculate estimated service count for a schedule
	// Mirrors backend logic: Mon-Thu use day flags, Fri uses weekend_service OR friday flag,
	// Sat-Sun use day flags only if weekend_service is false
	function estimateServiceCount(schedule: ScheduleForGeneration): number {
		const daysInMonth = getDaysInMonth(selectedMonth, selectedYear);
		let count = 0;

		for (let day = 1; day <= daysInMonth; day++) {
			const weekday = getWeekday(selectedYear, selectedMonth, day);

			// Check if schedule is active on this date
			const dateStr = `${selectedYear}-${String(selectedMonth).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
			if (schedule.startDate && dateStr < schedule.startDate) continue;
			if (schedule.endDate && dateStr > schedule.endDate) continue;

			let scheduleToday = false;

			if (weekday >= 0 && weekday <= 3) {
				// Mon-Thu: use the individual day flag
				if (weekday === 0) scheduleToday = schedule.monday;
				else if (weekday === 1) scheduleToday = schedule.tuesday;
				else if (weekday === 2) scheduleToday = schedule.wednesday;
				else if (weekday === 3) scheduleToday = schedule.thursday;
			} else if (weekday === 4) {
				// Friday: weekend_service takes precedence, otherwise use friday flag
				scheduleToday = schedule.weekendService || schedule.friday;
			} else {
				// Sat (5) or Sun (6): only use day flag if weekend_service is OFF
				if (!schedule.weekendService) {
					if (weekday === 5) scheduleToday = schedule.saturday;
					else if (weekday === 6) scheduleToday = schedule.sunday;
				}
			}

			if (scheduleToday) count++;
		}

		return count;
	}

	function formatScheduleDays(schedule: ScheduleForGeneration): string {
		const days: string[] = [];
		if (schedule.monday) days.push('M');
		if (schedule.tuesday) days.push('T');
		if (schedule.wednesday) days.push('W');
		if (schedule.thursday) days.push('Th');
		if (schedule.friday) days.push('F');
		if (schedule.saturday) days.push('Sa');
		if (schedule.sunday) days.push('Su');
		// Add Weekend indicator if weekend_service is on (and Friday not already shown)
		if (schedule.weekendService && !schedule.friday) days.push('Weekend');
		else if (schedule.weekendService) days.push('(+Wknd)');
		return days.join(', ') || 'No days';
	}

	let totalEstimatedCount = $derived(
		Array.from(selectedSchedules.values()).reduce(
			(sum, sel) => sum + estimateServiceCount(sel.schedule),
			0
		)
	);

	async function handleGenerate() {
		if (selectedSchedules.size === 0) return;

		generating = true;
		error = null;
		results = { success: 0, errors: [] };

		for (const [scheduleId, selection] of selectedSchedules) {
			try {
				const input: GenerateServicesInput = {
					accountAddressId: selection.accountAddressId,
					scheduleId,
					month: selectedMonth,
					year: selectedYear
				};

				const result = await client.mutate<GenerateServicesByMonthResult>({
					mutation: GENERATE_SERVICES_BY_MONTH,
					variables: { input }
				});

				if (result.data?.generateServicesByMonth) {
					results.success += result.data.generateServicesByMonth.length;
				}
			} catch (e) {
				const errMsg = e instanceof Error ? e.message : 'Unknown error';
				results.errors.push(`${selection.accountName} - ${selection.addressName}: ${errMsg}`);
			}
		}

		generating = false;

		if (results.errors.length === 0 && results.success > 0) {
			onSuccess?.();
		}
	}

	function handleKeydown(event: KeyboardEvent) {
		if (event.key === 'Escape') {
			onClose();
		}
	}

	// Lock body scroll when open
	$effect(() => {
		if (open) {
			document.body.style.overflow = 'hidden';
			return () => {
				document.body.style.overflow = '';
			};
		}
	});
</script>

<svelte:window onkeydown={handleKeydown} />

{#if open}
	<!-- Backdrop -->
	<button
		type="button"
		class="fixed inset-0 z-50 cursor-default bg-overlay backdrop-blur-sm"
		onclick={onClose}
		aria-label="Close modal"
		transition:fade={{ duration: 200 }}
	></button>

	<!-- Modal -->
	<div
		class="pointer-events-none fixed inset-0 z-50 flex items-center justify-center"
		role="dialog"
		aria-modal="true"
		aria-labelledby="generate-services-modal-title"
		tabindex="-1"
		transition:fade={{ duration: 200 }}
	>
		<div
			class="pointer-events-auto mx-4 w-full max-w-2xl rounded-xl border border-primary-500/20 bg-theme-card shadow-2xl"
			transition:scale={{ start: 0.95, duration: 200 }}
		>
			<!-- Header -->
			<div
				class="border-b border-primary-500/20 bg-linear-to-r from-primary-500/10 to-transparent px-5 py-4"
			>
				<h2 id="generate-services-modal-title" class="text-lg font-semibold text-theme">
					Generate Services
				</h2>
				<p class="mt-1 text-sm text-theme-secondary">
					Create services for a month based on schedules
				</p>
			</div>

			<!-- Body -->
			<div class="max-h-[60vh] overflow-y-auto px-5 py-5">
				{#if loading}
					<div class="flex items-center justify-center py-8">
						<div
							class="h-8 w-8 animate-spin rounded-full border-4 border-primary-500 border-t-transparent"
						></div>
					</div>
				{:else if error}
					<div class="rounded-lg bg-red-50 p-4 text-red-800 dark:bg-red-900/20 dark:text-red-400">
						{error}
					</div>
				{:else if results}
					<!-- Results view -->
					<div class="space-y-4">
						{#if results.success > 0}
							<div
								class="rounded-lg bg-primary-50 p-4 text-primary-800 dark:bg-primary-900/20 dark:text-primary-400"
							>
								Successfully created {results.success} service{results.success === 1 ? '' : 's'}.
							</div>
						{/if}
						{#if results.errors.length > 0}
							<div
								class="rounded-lg bg-red-50 p-4 text-red-800 dark:bg-red-900/20 dark:text-red-400"
							>
								<p class="font-medium">Some schedules failed:</p>
								<ul class="mt-2 list-inside list-disc space-y-1 text-sm">
									{#each results.errors as err}
										<li>{err}</li>
									{/each}
								</ul>
							</div>
						{/if}
					</div>
				{:else}
					<!-- Selection view -->
					<div class="space-y-4">
						<!-- Month/Year Selection -->
						<div class="flex gap-4">
							<label class="flex-1">
								<span class="mb-1 block text-sm font-medium text-theme-secondary">Month</span>
								<select
									class="w-full rounded-lg border border-theme bg-theme px-3 py-2 text-theme focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none"
									onchange={(e) => (selectedMonth = Number(e.currentTarget.value))}
								>
									{#each months as month}
										<option value={month.value} selected={selectedMonth === month.value}>
											{month.label}
										</option>
									{/each}
								</select>
							</label>
							<label class="w-32">
								<span class="mb-1 block text-sm font-medium text-theme-secondary">Year</span>
								<select
									class="w-full rounded-lg border border-theme bg-theme px-3 py-2 text-theme focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none"
									onchange={(e) => (selectedYear = Number(e.currentTarget.value))}
								>
									{#each years as year}
										<option value={year} selected={selectedYear === year}>
											{year}
										</option>
									{/each}
								</select>
							</label>
						</div>

						<!-- Select All / Clear -->
						<div class="flex items-center justify-between">
							<span class="text-sm text-theme-secondary">
								{selectedSchedules.size} schedule{selectedSchedules.size === 1 ? '' : 's'} selected
								{#if selectedSchedules.size > 0}
									({totalEstimatedCount} service{totalEstimatedCount === 1 ? '' : 's'})
								{/if}
							</span>
							<div class="flex gap-2">
								<button
									type="button"
									class="text-sm text-primary-500 hover:text-primary-600"
									onclick={selectAll}
								>
									Select All
								</button>
								<span class="text-theme-muted">|</span>
								<button
									type="button"
									class="text-sm text-theme-secondary hover:text-theme"
									onclick={clearAll}
								>
									Clear
								</button>
							</div>
						</div>

						<!-- Account Accordion -->
						{#if accounts.length === 0}
							<div class="py-4 text-center text-theme-muted">
								No active accounts with schedules found.
							</div>
						{:else}
							<div class="space-y-2">
								{#each accounts as account (account.id)}
									<div class="bg-theme-secondary/30 rounded-lg border border-theme">
										<!-- Account Header -->
										<button
											type="button"
											class="flex w-full items-center justify-between px-4 py-3 text-left"
											onclick={() => toggleAccount(account.id)}
										>
											<div>
												<span class="font-medium text-theme">{account.name}</span>
												{#if account.customer}
													<span class="ml-2 text-sm text-theme-muted">
														({account.customer.name})
													</span>
												{/if}
											</div>
											<svg
												class="h-5 w-5 shrink-0 text-theme-muted transition-transform {expandedAccounts.has(
													account.id
												)
													? 'rotate-180'
													: ''}"
												fill="none"
												stroke="currentColor"
												viewBox="0 0 24 24"
											>
												<path
													stroke-linecap="round"
													stroke-linejoin="round"
													stroke-width="2"
													d="M19 9l-7 7-7-7"
												/>
											</svg>
										</button>

										<!-- Account Content -->
										{#if expandedAccounts.has(account.id)}
											<div class="border-t border-theme px-4 pt-3 pb-4">
												{#each account.addresses as address (address.id)}
													{#if address.isActive && address.schedules.length > 0}
														<div class="mb-3 last:mb-0">
															<p class="mb-2 text-sm font-medium text-theme-secondary">
																{address.name || `${address.city}, ${address.state}`}
															</p>
															<div class="space-y-2 pl-2">
																{#each address.schedules as schedule (schedule.id)}
																	{@const estimated = estimateServiceCount(schedule)}
																	{@const isActive = isScheduleActive(schedule)}
																	<label
																		class="hover:bg-theme-secondary/50 flex cursor-pointer items-center gap-3 rounded-md px-2 py-1 {!isActive
																			? 'opacity-50'
																			: ''}"
																	>
																		<input
																			type="checkbox"
																			checked={selectedSchedules.has(schedule.id)}
																			disabled={!isActive}
																			onchange={() => toggleSchedule(schedule, address, account)}
																			class="h-4 w-4 rounded border-theme text-primary-500 focus:ring-primary-500"
																		/>
																		<span class="flex-1 text-sm text-theme">
																			{schedule.name || 'Service Schedule'}
																		</span>
																		<span
																			class="rounded-full bg-theme-secondary px-2 py-0.5 text-xs text-theme-muted"
																		>
																			{formatScheduleDays(schedule)}
																		</span>
																		<span
																			class="rounded-full bg-primary-100 px-2 py-0.5 text-xs font-medium text-primary-700 dark:bg-primary-900/30 dark:text-primary-400"
																		>
																			{estimated}
																		</span>
																	</label>
																{/each}
															</div>
														</div>
													{/if}
												{/each}
											</div>
										{/if}
									</div>
								{/each}
							</div>
						{/if}
					</div>
				{/if}
			</div>

			<!-- Footer -->
			<div class="flex justify-end gap-3 border-t border-theme px-5 py-4">
				{#if results}
					<button type="button" class="btn-primary" onclick={onClose}> Done </button>
				{:else}
					<button type="button" class="btn-secondary" onclick={onClose} disabled={generating}>
						Cancel
					</button>
					<button
						type="button"
						class="btn-primary"
						onclick={handleGenerate}
						disabled={generating || selectedSchedules.size === 0}
					>
						{#if generating}
							<span class="flex items-center gap-2">
								<span
									class="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent"
								></span>
								Generating...
							</span>
						{:else}
							Generate {totalEstimatedCount} Service{totalEstimatedCount === 1 ? '' : 's'}
						{/if}
					</button>
				{/if}
			</div>
		</div>
	</div>
{/if}
