<script lang="ts">
	import * as Dialog from '$lib/components/ui/dialog';
	import { Progress } from '$lib/components/ui/progress';
	import { invoicesReportsApi } from '$lib/api/dashboard/a76/reports/reports-invoices';
	import { Loader2, CheckCircle2, XCircle, FileDown } from 'lucide-svelte';
	import { Button } from '$lib/components/ui/button';
	import { friendlyApiErrorParts, humanizeLineReferences, type ApiResponse } from '$lib/api';

	export let open = false;
	export let taskId: string | null = null;
	export let onClose: () => void;
	export let onComplete: (result: any) => void;
	export let title: string = 'Generando PDF';

	/** Optional: steps for step-by-step progress (e.g. invoice processing). Each step has { label, percent } */
	export let steps: { label: string; percent: number }[] = [];

	/** Optional: message when complete (default: "Listo para descargar") */
	export let completeMessage: string = 'Listo para descargar';

	export let getStatus: ((taskId: string) => Promise<any>) | null = null;

	type DialogStatus = 'idle' | 'running' | 'success' | 'error' | 'validation_error';

	let progress = 0;
	let statusMessage = 'Iniciando...';
	let pollingInterval: any = null;
	let dialogStatus: DialogStatus = 'idle';
	let lastResult: any = null;
	let externalBody: any = null;
	let externalErrors: any[] | null = null;
	let errorTitle = 'Ocurrió un error';
	let errorDescription = 'No se pudo completar la acción.';
	let errorList: string[] = [];

	$: isRunning = dialogStatus === 'running';
	$: isSuccess = dialogStatus === 'success';
	$: isError = dialogStatus === 'error' || dialogStatus === 'validation_error';
	$: completedStepIndex =
		steps.length === 0
			? -1
			: Math.max(
				steps.findLastIndex((step) => progress >= step.percent),
				isSuccess ? steps.length - 1 : -1
			  );
	$: activeStepIndex =
		steps.length === 0
			? -1
			: Math.min(
				steps.findIndex((step) => progress < step.percent) === -1
					? steps.length - 1
					: steps.findIndex((step) => progress < step.percent),
				steps.length - 1
		  );

	// Reiniciar estado cuando se abre el diálogo con un nuevo taskId
	$: if (open && taskId) {
		resetState();
		startPolling();
	} else if (!open) {
		stopPolling();
	}

	// Derivar cuerpo y lista de errores del API externo (si existen)
	$: externalBody = lastResult?.external_response?.body ?? null;
	$: externalErrors = externalBody?.errores ?? null;

	function stopPolling() {
		if (pollingInterval) {
			clearInterval(pollingInterval);
			pollingInterval = null;
		}
	}

	function resetState() {
		progress = 0;
		statusMessage = 'Iniciando...';
		dialogStatus = 'running';
		lastResult = null;
		externalBody = null;
		externalErrors = null;
		errorTitle = 'Ocurrió un error';
		errorDescription = 'No se pudo completar la acción.';
		errorList = [];
	}

	function normalizeErrorText(value: unknown, fallback: string) {
		if (typeof value !== 'string') return fallback;
		const normalized = humanizeLineReferences(value.trim());
		return normalized || fallback;
	}

	function buildErrorPresentation(
		source?: Partial<ApiResponse> | null,
		result?: any
	): { status: DialogStatus; title: string; description: string; list: string[] } {
		const status: DialogStatus =
			result?.status === 'validation_error' ? 'validation_error' : 'error';
		const validationErrors = source?.validationErrors || result?.validationErrors;

		if (validationErrors?.length) {
			const { title, description } = friendlyApiErrorParts({
				status: source?.status ?? 400,
				error: source?.error || result?.message || 'Error de validación',
				validationErrors
			});
			return {
				status,
				title,
				description,
				list: [] as string[]
			};
		}

		const resultErrors = Array.isArray(result?.errors)
			? result.errors
				.map((entry: any) => normalizeErrorText(entry?.message || String(entry), 'Error de validación'))
				.filter(Boolean)
			: [];

		if (resultErrors.length > 0) {
			return {
				status,
				title:
					status === 'validation_error'
						? 'Se encontraron errores de validación'
						: 'No se pudo completar la acción',
				description:
					normalizeErrorText(result?.message, resultErrors[0] || 'Revisa los datos e intenta de nuevo.'),
				list: resultErrors
			};
		}

		const fallbackDescription = normalizeErrorText(
			source?.error || result?.message,
			status === 'validation_error'
				? 'Se encontraron errores de validación. Revisa los datos e intenta de nuevo.'
				: 'No se pudo completar la acción. Intenta de nuevo.'
		);

		return {
			status,
			title:
				status === 'validation_error'
					? 'Se encontraron errores de validación'
					: 'No se pudo completar la acción',
			description: fallbackDescription,
			list: [] as string[]
		};
	}

	function setErrorState(source?: Partial<ApiResponse> | null, result?: any) {
		const presentation = buildErrorPresentation(source, result);
		lastResult = result ?? lastResult;
		dialogStatus = presentation.status;
		errorTitle = presentation.title;
		errorDescription = presentation.description;
		errorList = presentation.list;
		statusMessage = presentation.description;
		stopPolling();
	}

	async function pollOnce() {
		if (!taskId) return;
		const apiCall = getStatus || invoicesReportsApi.getTaskStatus;
		const raw = await apiCall(taskId);
		const response = raw?.data !== undefined ? raw.data : raw;
		if (raw?.error) {
			setErrorState(raw, response);
			return;
		}
		if (response?.state === 'PROCESSING' && response.info) {
			progress = response.info.current || 0;
			statusMessage = response.info.status || 'Procesando...';
		} else if (response?.state === 'SUCCESS') {
			const result = response.result;
			lastResult = result;

			if (result?.status === 'validation_error' || result?.status === 'error') {
				setErrorState(
					{
						status: response?.status ?? 400,
						error: result?.message,
						validationErrors: result?.validationErrors
					},
					result
				);
				onComplete(result);
			} else {
				progress = 100;
				statusMessage = '¡Completado!';
				dialogStatus = 'success';
				stopPolling();
				onComplete(result);
			}
		} else if (response?.state === 'FAILURE') {
			const errMsg = response.result ? String(response.result) : 'Error desconocido';
			setErrorState({ status: response?.status ?? 500, error: errMsg }, response?.result);
		}
	}

	async function startPolling() {
		stopPolling(); // Asegurar limpieza previa
		try {
			await pollOnce(); // Primer poll inmediato para mostrar progreso sin esperar 1s
		} catch (error) {
			console.error('Error polling task status:', error);
			setErrorState({ status: 500, error: error instanceof Error ? error.message : String(error) });
			return;
		}

		pollingInterval = setInterval(async () => {
			if (!isRunning) return;
			try {
				await pollOnce();
			} catch (error) {
				console.error('Error polling task status:', error);
				setErrorState({ status: 500, error: error instanceof Error ? error.message : String(error) });
			}
		}, 1000);
	}

	function handleOpenChange(newOpen: boolean) {
		if (!newOpen) {
			stopPolling();
			onClose();
		}
	}
</script>

<Dialog.Root bind:open onOpenChange={handleOpenChange}>
	<Dialog.Content class="sm:max-w-[425px]">
		<Dialog.Header>
			<Dialog.Title>{title}</Dialog.Title>
			<Dialog.Description>Por favor espere mientras se genera su documento.</Dialog.Description>
		</Dialog.Header>

		<div class="flex flex-col gap-6 py-6">
			{#if steps.length > 0}
				<!-- Lista de pasos con progreso -->
				<div class="space-y-2">
					{#each steps as step, i}
						{@const isDone = i < activeStepIndex || (isSuccess && progress >= step.percent)}
						{@const isReached = i <= completedStepIndex}
						{@const isCurrent = isRunning && i === activeStepIndex}
						{@const isFailed = isError && i === activeStepIndex}
						<div
							class="flex items-center gap-3 rounded-md border px-3 py-2 text-sm transition-colors {isDone
								? 'border-green-200 bg-green-50 dark:border-green-900 dark:bg-green-950/30'
								: isFailed
									? 'border-destructive/40 bg-destructive/5'
								: isReached
									? 'border-primary/30 bg-primary/5'
								: isCurrent
									? 'border-primary/50 bg-primary/5'
									: 'border-border/50 bg-muted/30 opacity-60'}"
						>
							<span class="flex-shrink-0 w-6 text-center font-medium text-muted-foreground">
								{i + 1}.
							</span>
							<span
								class="flex-1 {isDone
									? 'text-green-700 dark:text-green-400'
									: isFailed
										? 'text-destructive'
										: isReached
											? 'text-primary'
										: ''}"
							>
								{step.label}
							</span>
							<span class="flex-shrink-0 font-medium tabular-nums">
								{#if isDone}
									<span class="text-green-600 dark:text-green-400">100%</span>
								{:else if isFailed}
									<span class="text-destructive">{progress}%</span>
								{:else if isReached}
									<span class="text-primary">100%</span>
								{:else if isCurrent}
									<span class="text-primary">{progress}%</span>
								{:else}
									<span class="text-muted-foreground">0%</span>
								{/if}
							</span>
							{#if isDone}
								<CheckCircle2 class="h-4 w-4 flex-shrink-0 text-green-600" />
							{:else if isFailed}
								<XCircle class="h-4 w-4 flex-shrink-0 text-destructive" />
							{:else if isReached}
								<CheckCircle2 class="h-4 w-4 flex-shrink-0 text-primary" />
							{:else if isCurrent}
								<Loader2 class="h-4 w-4 flex-shrink-0 animate-spin text-primary" />
							{/if}
						</div>
					{/each}
				</div>
				<div class="px-3">
					<Progress value={progress} class="h-1.5 w-full" />
				</div>
				{#if statusMessage}
					<div class="mt-1 px-3 text-xs {isError ? 'text-destructive' : 'text-muted-foreground'}">
						{statusMessage}
					</div>
				{/if}
			{:else}
				<div class="mb-1 flex items-center justify-between text-sm">
					<span class={isError ? 'text-destructive' : 'text-muted-foreground'}>{statusMessage}</span>
					<span class="font-medium">{progress}%</span>
				</div>
				<Progress value={progress} class="h-2 w-full" />
			{/if}

			<div
				class="flex justify-center {isError
					? 'min-h-0 items-start'
					: 'min-h-16 items-center'}"
			>
				{#if isSuccess}
					<div class="animate-in fade-in zoom-in flex flex-col items-center text-green-600 duration-300">
						<CheckCircle2 size={48} />
						<span class="mt-2 text-sm font-medium">
							{#if lastResult?.cove_number}
								COVE generado
							{:else}
								{completeMessage}
							{/if}
						</span>

						{#if lastResult?.cove_number}
							<span class="mt-1 text-xs text-muted-foreground break-all">
								COVE: {lastResult.cove_number}
							</span>
						{/if}
						{#if lastResult?.external_task_id}
							<span class="mt-1 text-[11px] text-muted-foreground break-all">
								Task ID COVE: {lastResult.external_task_id}
							</span>
						{:else if taskId}
							<span class="mt-1 text-[11px] text-muted-foreground break-all">
								Task interno: {taskId}
							</span>
						{/if}

						{#if lastResult?.message}
							<span class="mt-2 text-xs text-muted-foreground text-center whitespace-pre-line">
								{lastResult.message}
							</span>
						{/if}
					</div>
				{:else if isError}
					<div
						class="animate-in fade-in zoom-in flex w-full flex-col items-center text-destructive duration-300"
					>
						<XCircle size={48} />
						<div class="mt-3 w-full rounded-lg border border-destructive/40 bg-destructive/5 p-4 text-left">
							<p class="text-sm font-semibold">{errorTitle}</p>
							<p class="mt-1 text-xs whitespace-pre-line">{errorDescription}</p>
							{#if errorList.length > 0}
								<ul class="mt-3 max-h-32 overflow-auto list-disc space-y-1 pl-4 text-xs">
									{#each errorList as item}
										<li>{item}</li>
									{/each}
								</ul>
							{/if}
							{#if dialogStatus === 'validation_error' && externalBody}
								<p class="mt-3 text-xs font-medium">
									{externalBody.mensaje || 'Datos inválidos en el servicio COVE.'}
								</p>
								{#if externalErrors && externalErrors.length}
									<ul class="mt-2 max-h-32 overflow-auto list-disc space-y-1 pl-4 text-xs">
										{#each externalErrors as e}
											<li>{e.campo}: {e.mensaje}</li>
										{/each}
									</ul>
								{/if}
							{/if}
						</div>
					</div>
				{:else}
					<div class="flex animate-pulse flex-col items-center text-primary">
						<FileDown size={48} class="opacity-50" />
					</div>
				{/if}
			</div>
		</div>

		<Dialog.Footer>
			{#if isSuccess || isError}
				<Button variant="secondary" onclick={() => (open = false)}>Cerrar</Button>
			{/if}
		</Dialog.Footer>
	</Dialog.Content>
</Dialog.Root>
