<script lang="ts">
	import { Tags, Plus, Pencil, Trash2, Search } from '@lucide/svelte';
	import * as Card from '$lib/components/ui/card';
	import * as Table from '$lib/components/ui/table';
	import { Button } from '$lib/components/ui/button';
	import { companyStore } from '$lib/stores/company.svelte';
	import {
		conceptsAPI,
		satCatalogsAPI,
		type Concept,
		type ConceptInput,
		type SatCatalogItem,
		type SatUnitOfMeasure
	} from '$lib/api/fin';
	import { toast } from 'svelte-sonner';

	const EMPTY_FORM: ConceptInput = {
		code: '',
		description: '',
		product_service_id: 0,
		unit_of_measure_id: null,
		tax_object_id: null,
		unit_price: null,
		currency: 'MXN',
		is_active: true,
		notes: ''
	};

	let items = $state<Concept[]>([]);
	let loading = $state(false);
	let search = $state('');
	let activeFilter = $state<'todos' | 'activos' | 'inactivos'>('todos');

	let modalOpen = $state(false);
	let saving = $state(false);
	let editingId = $state<number | null>(null);
	let form = $state<ConceptInput>({ ...EMPTY_FORM });
	/** Error del 409 del backend, mostrado junto al campo de clave ProdServ. */
	let productServiceError = $state('');

	// Catálogos del SAT para los selectores.
	let unitsOfMeasure = $state<SatUnitOfMeasure[]>([]);
	let taxObjects = $state<SatCatalogItem[]>([]);

	// Combobox de clave de producto/servicio.
	let productServiceQuery = $state('');
	let productServiceOptions = $state<SatCatalogItem[]>([]);
	let productServiceSelected = $state<SatCatalogItem | null>(null);
	let searchingProductService = $state(false);

	const companyId = $derived(companyStore.activeCompany?.id ?? null);

	$effect(() => {
		const cid = companyId;
		if (!cid) return;
		void load(cid);
		void loadCatalogs(cid);
	});

	async function load(cid: number) {
		loading = true;
		try {
			items = await conceptsAPI.list(cid, {
				search: search.trim() || undefined,
				active_only: activeFilter === 'todos' ? undefined : activeFilter === 'activos'
			});
		} catch (e) {
			toast.error(e instanceof Error ? e.message : 'No se pudieron cargar los conceptos');
		} finally {
			loading = false;
		}
	}

	async function loadCatalogs(cid: number) {
		try {
			[unitsOfMeasure, taxObjects] = await Promise.all([
				satCatalogsAPI.unitsOfMeasure(cid),
				satCatalogsAPI.taxObjects(cid)
			]);
		} catch (e) {
			toast.error(e instanceof Error ? e.message : 'No se pudieron cargar los catálogos del SAT');
		}
	}

	/** Busca claves ProdServ; a partir de 2 caracteres para no traer el catálogo completo. */
	async function searchProductServices() {
		const cid = companyId;
		const term = productServiceQuery.trim();
		if (!cid || term.length < 2) {
			productServiceOptions = [];
			return;
		}
		searchingProductService = true;
		try {
			productServiceOptions = await satCatalogsAPI.productsServices(cid, {
				search: term,
				limit: 20
			});
		} catch (e) {
			toast.error(
				e instanceof Error ? e.message : 'No se pudo buscar la clave de producto/servicio'
			);
		} finally {
			searchingProductService = false;
		}
	}

	function pickProductService(option: SatCatalogItem) {
		productServiceSelected = option;
		form.product_service_id = option.id;
		productServiceQuery = '';
		productServiceOptions = [];
		productServiceError = '';
	}

	function openCreate() {
		editingId = null;
		form = { ...EMPTY_FORM };
		productServiceSelected = null;
		productServiceQuery = '';
		productServiceOptions = [];
		productServiceError = '';
		modalOpen = true;
	}

	function openEdit(concept: Concept) {
		editingId = concept.id;
		form = {
			code: concept.code,
			description: concept.description,
			product_service_id: concept.product_service_id,
			unit_of_measure_id: concept.unit_of_measure_id,
			tax_object_id: concept.tax_object_id,
			unit_price: concept.unit_price,
			currency: concept.currency,
			is_active: concept.is_active,
			notes: concept.notes ?? ''
		};
		productServiceSelected = concept.product_service;
		productServiceQuery = '';
		productServiceOptions = [];
		productServiceError = '';
		modalOpen = true;
	}

	async function save(event: SubmitEvent) {
		event.preventDefault();
		const cid = companyId;
		if (!cid) return;
		if (!form.code.trim() || !form.description.trim()) {
			toast.error('La clave y la descripción son obligatorias');
			return;
		}
		if (!form.product_service_id) {
			productServiceError = 'Selecciona la clave de producto/servicio del SAT';
			return;
		}
		saving = true;
		productServiceError = '';
		try {
			const payload: ConceptInput = {
				...form,
				unit_price:
					form.unit_price === null || form.unit_price === undefined
						? null
						: Number(form.unit_price),
				notes: form.notes?.trim() ? form.notes : null
			};
			if (editingId) {
				await conceptsAPI.update(editingId, payload, cid);
				toast.success('Concepto actualizado');
			} else {
				await conceptsAPI.create(payload, cid);
				toast.success('Concepto creado');
			}
			modalOpen = false;
			await load(cid);
		} catch (e) {
			const message = e instanceof Error ? e.message : 'No se pudo guardar el concepto';
			// El 409 del backend por clave ProdServ ya tomada se muestra junto al campo.
			if (message.toLowerCase().includes('producto/servicio')) productServiceError = message;
			else toast.error(message);
		} finally {
			saving = false;
		}
	}

	async function remove(concept: Concept) {
		const cid = companyId;
		if (!cid) return;
		if (!confirm(`¿Dar de baja el concepto "${concept.code}"?`)) return;
		try {
			await conceptsAPI.remove(concept.id, cid);
			toast.success('Concepto dado de baja');
			await load(cid);
		} catch (e) {
			toast.error(e instanceof Error ? e.message : 'No se pudo dar de baja el concepto');
		}
	}

	function money(value: number | null): string {
		if (value === null || value === undefined) return '—';
		return new Intl.NumberFormat('es-MX', { minimumFractionDigits: 2 }).format(Number(value));
	}

	const inputCls =
		'rounded-md border bg-transparent px-3 py-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring';
</script>

<svelte:head>
	<title>Conceptos de facturación</title>
</svelte:head>

<div class="space-y-6">
	<div class="flex flex-wrap items-center justify-between gap-3">
		<div>
			<h1 class="flex items-center gap-2 text-2xl font-bold tracking-tight">
				<Tags class="h-6 w-6" />
				Conceptos de facturación
			</h1>
			<p class="mt-1 text-sm text-muted-foreground">
				Cada concepto se liga a una clave de producto/servicio del SAT, que no puede repetirse en la
				empresa.
			</p>
		</div>
		<Button onclick={openCreate} disabled={!companyId}>
			<Plus class="mr-1 h-4 w-4" /> Nuevo concepto
		</Button>
	</div>

	<Card.Root>
		<Card.Header>
			<div class="flex flex-wrap items-center gap-3">
				<div class="relative max-w-sm flex-1">
					<Search class="absolute top-2.5 left-2.5 h-4 w-4 text-muted-foreground" />
					<input
						class="w-full py-2 pr-3 pl-8 {inputCls}"
						placeholder="Buscar por clave o descripción…"
						bind:value={search}
						onchange={() => companyId && load(companyId)}
					/>
				</div>
				<select
					class="{inputCls} max-w-xs"
					bind:value={activeFilter}
					onchange={() => companyId && load(companyId)}
				>
					<option value="todos">Todos</option>
					<option value="activos">Solo activos</option>
					<option value="inactivos">Solo inactivos</option>
				</select>
			</div>
		</Card.Header>
		<Card.Content>
			{#if loading}
				<p class="py-6 text-center text-sm text-muted-foreground">Cargando…</p>
			{:else if items.length === 0}
				<p class="py-6 text-center text-sm text-muted-foreground">Sin conceptos registrados.</p>
			{:else}
				<div class="overflow-x-auto">
					<Table.Root>
						<Table.Header>
							<Table.Row>
								<Table.Head>Clave</Table.Head>
								<Table.Head>Descripción</Table.Head>
								<Table.Head>Clave ProdServ</Table.Head>
								<Table.Head>Unidad</Table.Head>
								<Table.Head>Objeto de impuesto</Table.Head>
								<Table.Head class="text-right">Precio unitario</Table.Head>
								<Table.Head>Estado</Table.Head>
								<Table.Head class="text-right">Acciones</Table.Head>
							</Table.Row>
						</Table.Header>
						<Table.Body>
							{#each items as concept (concept.id)}
								<Table.Row>
									<Table.Cell class="font-mono text-xs font-medium">{concept.code}</Table.Cell>
									<Table.Cell>{concept.description}</Table.Cell>
									<Table.Cell class="text-xs">
										<span class="font-mono">{concept.product_service?.code ?? '—'}</span>
										{#if concept.product_service}
											<span class="block text-muted-foreground"
												>{concept.product_service.description}</span
											>
										{/if}
									</Table.Cell>
									<Table.Cell class="text-xs">{concept.unit_of_measure?.name ?? '—'}</Table.Cell>
									<Table.Cell class="text-xs">{concept.tax_object?.code ?? '—'}</Table.Cell>
									<Table.Cell class="text-right"
										>{money(concept.unit_price)} {concept.currency}</Table.Cell
									>
									<Table.Cell>
										<span
											class="inline-flex rounded-full px-2 py-0.5 text-xs font-medium {concept.is_active
												? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-400'
												: 'bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400'}"
										>
											{concept.is_active ? 'Activo' : 'Inactivo'}
										</span>
									</Table.Cell>
									<Table.Cell class="text-right">
										<Button
											variant="ghost"
											size="sm"
											onclick={() => openEdit(concept)}
											aria-label="Editar"
										>
											<Pencil class="h-4 w-4" />
										</Button>
										<Button
											variant="ghost"
											size="sm"
											onclick={() => remove(concept)}
											aria-label="Dar de baja"
										>
											<Trash2 class="h-4 w-4 text-destructive" />
										</Button>
									</Table.Cell>
								</Table.Row>
							{/each}
						</Table.Body>
					</Table.Root>
				</div>
			{/if}
		</Card.Content>
	</Card.Root>
</div>

<svelte:window
	onkeydown={(e) => {
		if (modalOpen && e.key === 'Escape') modalOpen = false;
	}}
/>

{#if modalOpen}
	<div
		class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
		role="presentation"
		onclick={() => (modalOpen = false)}
	>
		<div
			class="max-h-[90vh] w-full max-w-2xl overflow-y-auto rounded-lg border bg-card p-6 shadow-lg"
			role="dialog"
			aria-modal="true"
			aria-label={editingId ? 'Editar concepto' : 'Nuevo concepto'}
			tabindex="-1"
			onclick={(e) => e.stopPropagation()}
			onkeydown={(e) => e.stopPropagation()}
		>
			<h2 class="mb-4 text-lg font-semibold">{editingId ? 'Editar concepto' : 'Nuevo concepto'}</h2>
			<form class="grid gap-4 sm:grid-cols-2" onsubmit={save}>
				<label class="flex flex-col gap-1 text-sm">
					<span class="font-medium">Clave *</span>
					<input class={inputCls} bind:value={form.code} maxlength="40" required />
				</label>
				<label class="flex flex-col gap-1 text-sm">
					<span class="font-medium">Precio unitario</span>
					<input type="number" step="0.01" min="0" class={inputCls} bind:value={form.unit_price} />
				</label>
				<label class="flex flex-col gap-1 text-sm sm:col-span-2">
					<span class="font-medium">Descripción *</span>
					<input class={inputCls} bind:value={form.description} maxlength="500" required />
				</label>

				<div class="flex flex-col gap-1 text-sm sm:col-span-2">
					<span class="font-medium">Clave de producto/servicio del SAT *</span>
					{#if productServiceSelected}
						<div class="flex items-center justify-between gap-2 rounded-md border px-3 py-2">
							<span class="text-sm">
								<span class="font-mono">{productServiceSelected.code}</span>
								<span class="text-muted-foreground"> — {productServiceSelected.description}</span>
							</span>
							<Button
								type="button"
								variant="ghost"
								size="sm"
								onclick={() => {
									productServiceSelected = null;
									form.product_service_id = 0;
								}}
							>
								Cambiar
							</Button>
						</div>
					{:else}
						<input
							class={inputCls}
							placeholder="Escribe al menos 2 caracteres (clave o descripción)…"
							bind:value={productServiceQuery}
							oninput={searchProductServices}
						/>
						{#if searchingProductService}
							<p class="text-xs text-muted-foreground">Buscando…</p>
						{:else if productServiceOptions.length > 0}
							<ul class="max-h-48 overflow-y-auto rounded-md border">
								{#each productServiceOptions as option (option.id)}
									<li>
										<button
											type="button"
											class="w-full px-3 py-2 text-left text-sm hover:bg-muted"
											onclick={() => pickProductService(option)}
										>
											<span class="font-mono">{option.code}</span>
											<span class="text-muted-foreground"> — {option.description}</span>
										</button>
									</li>
								{/each}
							</ul>
						{/if}
					{/if}
					{#if productServiceError}
						<p class="text-xs text-destructive">{productServiceError}</p>
					{/if}
				</div>

				<label class="flex flex-col gap-1 text-sm">
					<span class="font-medium">Unidad de medida</span>
					<select class={inputCls} bind:value={form.unit_of_measure_id}>
						<option value={null}>Sin especificar</option>
						{#each unitsOfMeasure as unit (unit.id)}
							<option value={unit.id}>{unit.code} — {unit.name}</option>
						{/each}
					</select>
				</label>
				<label class="flex flex-col gap-1 text-sm">
					<span class="font-medium">Objeto de impuesto</span>
					<select class={inputCls} bind:value={form.tax_object_id}>
						<option value={null}>Sin especificar</option>
						{#each taxObjects as taxObject (taxObject.id)}
							<option value={taxObject.id}>{taxObject.code} — {taxObject.description}</option>
						{/each}
					</select>
				</label>

				<label class="flex flex-col gap-1 text-sm">
					<span class="font-medium">Moneda</span>
					<input class={inputCls} bind:value={form.currency} maxlength="3" />
				</label>
				<label class="flex items-center gap-2 self-end text-sm">
					<input type="checkbox" bind:checked={form.is_active} />
					<span class="font-medium">Activo</span>
				</label>

				<label class="flex flex-col gap-1 text-sm sm:col-span-2">
					<span class="font-medium">Notas</span>
					<textarea rows="2" class={inputCls} bind:value={form.notes}></textarea>
				</label>

				<div class="flex justify-end gap-2 sm:col-span-2">
					<Button type="button" variant="outline" onclick={() => (modalOpen = false)}
						>Cancelar</Button
					>
					<Button type="submit" disabled={saving}>{saving ? 'Guardando…' : 'Guardar'}</Button>
				</div>
			</form>
		</div>
	</div>
{/if}
