/**
 * Cliente API para comunicación con el backend
 */
import { getToken } from './auth';
import { browser } from '$app/environment';
import { toast } from 'svelte-sonner';

// Normalize API_BASE_URL to remove trailing slash
const API_BASE_URL = (import.meta.env.VITE_API_URL || '').replace(/\/+$/, '');

export interface ApiResponse<T = any> {
	data?: T;
	error?: string;
	validationErrors?: Array<{
		field: string;
		message: string;
		code?: string;
		solution?: string[];
		value?: any;
	}>;
	status: number;
}

let isRefreshing = false;
let refreshSubscribers: ((token: string) => void)[] = [];

/**
 * Agrega una petición a la cola de espera mientras se refresca el token
 */
function subscribeTokenRefresh(callback: (token: string) => void) {
	refreshSubscribers.push(callback);
}

/**
 * Notifica a todas las peticiones en espera que el token se ha refrescado
 */
function onTokenRefreshed(token: string) {
	refreshSubscribers.forEach((callback) => callback(token));
	refreshSubscribers = [];
}

/**
 * Refresca el token silenciosamente usando el endpoint server-side.
 *
 * El servidor lee el refresh_token desde la cookie HttpOnly,
 * llama a Keycloak, actualiza las cookies y devuelve el nuevo access_token.
 * El refresh_token NUNCA es leído por este código JavaScript.
 */
async function refreshToken(): Promise<string | null> {
	if (!browser) return null;

	try {
		const response = await fetch('/api-sveltekit/auth/silent-refresh', {
			method: 'POST',
			credentials: 'include', // Envía cookies HttpOnly automáticamente
			headers: { 'Content-Type': 'application/json' }
		});

		if (!response.ok) {
			console.error('❌ [API] Silent refresh falló, status:', response.status);
			// Limpiar la cookie del access_token (no HttpOnly) para forzar re-login
			document.cookie = 'access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC';
			setTimeout(() => { window.location.href = '/login'; }, 1500);
			return null;
		}

		const data = await response.json() as { access_token?: string };

		if (data.access_token) {
			// Actualizar cookie no-HttpOnly del access_token
			const secure = window.location.protocol === 'https:' ? '; Secure' : '';
			document.cookie = `access_token=${data.access_token}; path=/; max-age=${60 * 60 * 24 * 7}; SameSite=Lax${secure}`;

			// Actualizar authStore en memoria
			try {
				const { authStore } = await import('./auth');
				authStore.setToken(data.access_token);
			} catch {}

			return data.access_token;
		}

		return null;
	} catch (error) {
		console.error('❌ [API] Error en silent refresh:', error);
		return null;
	}
}

/**
 * Realiza una petición al API con manejo automático de refresh token
 */
async function fetchApi<T = any>(
	endpoint: string,
	options: RequestInit = {},
	retryCount = 0
): Promise<ApiResponse<T>> {
	// Si ya estamos refrescando el token, esperar
	if (isRefreshing && retryCount === 0) {
		return new Promise((resolve) => {
			subscribeTokenRefresh((newToken) => {
				resolve(fetchApi<T>(endpoint, options, 1));
			});
		});
	}

	const token = getToken();

	if (!token && !endpoint.includes('/auth/login')) {
		console.warn('⚠️ [API] No hay token disponible para', endpoint);
	}

	const headers: Record<string, string> = {
		...((options.headers as Record<string, string>) || {})
	};

	// Only set Content-Type to application/json if not already set and body is not FormData
	if (!headers['Content-Type'] && !(options.body instanceof FormData)) {
		headers['Content-Type'] = 'application/json';
	}

	if (token) {
		headers['Authorization'] = `Bearer ${token}`;
	}

	try {
		const response = await fetch(`${API_BASE_URL}${endpoint}`, {
			...options,
			headers,
			credentials: 'include' // Importante: envía cookies con cada request
		});

		// Si recibimos 401 o 403 y no es el endpoint de refresh, intentar refrescar el token
		if ((response.status === 401 || response.status === 403) && !endpoint.includes('/auth/refresh') && retryCount === 0) {
			// Si es 403 (Forbidden), mostrar toast de permisos insuficientes
			if (response.status === 403) {
				if (browser) {
					toast.error('No tienes permisos para realizar esta acción', {
						duration: 4000,
						description: 'Contacta a tu administrador si crees que esto es un error'
					});
				}
				// Retornar el error 403 sin intentar refresh
				const data = await response.json();
				return {
					error: data.detail || 'No tienes permisos para realizar esta acción',
					status: 403
				};
			}

			// Si es 401, intentar refrescar el token
			isRefreshing = true;

			try {
				const newToken = await refreshToken();

				if (newToken) {
					// Token refrescado exitosamente					
					onTokenRefreshed(newToken);
					isRefreshing = false;
					// Reintentar la petición original con el nuevo token
					return await fetchApi<T>(endpoint, options, 1);
				} else {
					console.error('❌ [API] No se pudo refrescar el token');
					isRefreshing = false;
					// Retornar error 401 para que la capa superior lo maneje
					return {
						error: 'Sesión expirada. Por favor, inicia sesión nuevamente.',
						status: 401
					};
				}
			} catch (refreshError) {
				console.error('❌ [API] Error al refrescar:', refreshError);
				isRefreshing = false;
				return {
					error: 'Error al refrescar la sesión',
					status: 401
				};
			}
		}

		// Manejar respuestas sin contenido (204 No Content)
		if (response.status === 204) {
			return {
				data: null as T,
				status: response.status
			};
		}

		const data = await response.json();

		if (!response.ok) {
			// Manejo especial para errores 422 (validation error)
			if (response.status === 422) {
				// Errores de validación personalizados (con array errors)
				if (data.errors && Array.isArray(data.errors)) {
					return {
						error: data.message || 'Error de validación',
						validationErrors: data.errors,
						status: response.status
					};
				}
				// Errores de validación de FastAPI (con detail)
				else if (data.detail) {
					let errorMessage = 'Error de validación: ';

					// FastAPI devuelve errores de validación en data.detail como array
					if (Array.isArray(data.detail)) {
						const errors = data.detail.map((err: any) => {
							const field = err.loc ? err.loc.join('.') : 'campo desconocido';
							return `${field}: ${err.msg}`;
						}).join(', ');
						errorMessage += errors;
					} else if (typeof data.detail === 'string') {
						errorMessage = data.detail;
					} else {
						errorMessage += JSON.stringify(data.detail);
					}

					return {
						error: errorMessage,
						status: response.status
					};
				}
			}

			return {
				error: data.message || (typeof data.detail === 'string' ? data.detail : JSON.stringify(data.detail)) || 'Error en la petición',
				status: response.status
			};
		}

		return {
			data,
			status: response.status
		};
	} catch (error) {
		console.error(`❌ [API] Error de conexión en ${endpoint}:`, error);
		return {
			error: 'Error de conexión con el servidor',
			status: 0
		};
	}
}

async function fetchBlob(endpoint: string, options: RequestInit = {}): Promise<Blob> {
	const token = getToken();
	const headers: Record<string, string> = {
		...((options.headers as Record<string, string>) || {})
	};
	if (token) {
		headers['Authorization'] = `Bearer ${token}`;
	}

	const response = await fetch(`${API_BASE_URL}${endpoint}`, {
		...options,
		headers,
		credentials: 'include'
	});

	if (!response.ok) {
		// Try to extract some useful message for debugging/UI.
		const text = await response.text().catch(() => '');
		throw new Error(text || `Error ${response.status} descargando archivo`);
	}
	return await response.blob();
}

// Métodos HTTP
export const api = {
	get: <T = any>(endpoint: string) => fetchApi<T>(endpoint, { method: 'GET' }),

	post: <T = any>(endpoint: string, body: any, options: RequestInit = {}) =>
		fetchApi<T>(endpoint, {
			method: 'POST',
			body: JSON.stringify(body),
			...options
		}),

	put: <T = any>(endpoint: string, body: any, options: RequestInit = {}) =>
		fetchApi<T>(endpoint, {
			method: 'PUT',
			body: JSON.stringify(body),
			...options
		}),

	patch: <T = any>(endpoint: string, body: any, options: RequestInit = {}) =>
		fetchApi<T>(endpoint, {
			method: 'PATCH',
			body: JSON.stringify(body),
			...options
		}),

	delete: <T = any>(endpoint: string, options: RequestInit = {}) => fetchApi<T>(endpoint, { method: 'DELETE', ...options }),

	/**
	 * Download CSV template by template_id (generated from code, no static file).
	 * Returns blob and suggested filename for the browser download.
	 */
	async getCsvTemplateDownload(
		templateId: string
	): Promise<{ blob: Blob; filename: string }> {
		const token = getToken();
		const headers: Record<string, string> = {};
		if (token) headers['Authorization'] = `Bearer ${token}`;
		const response = await fetch(`${API_BASE_URL}/v1/a76/csv-templates/${templateId}`, {
			method: 'GET',
			headers,
			credentials: 'include'
		});
		if (!response.ok) {
			const msg = response.status === 404 ? 'Plantilla no encontrada' : `Error ${response.status}`;
			throw new Error(msg);
		}
		const blob = await response.blob();
		let filename = `plantilla_${templateId}.csv`;
		const disposition = response.headers.get('Content-Disposition');
		if (disposition) {
			const match = /filename="?([^";\n]+)"?/.exec(disposition);
			if (match) filename = match[1].trim();
		}
		return { blob, filename };
	},

	// Endpoints específicos
	auth: {
		login: (credentials: { username: string; password: string; tenant_slug: string }) =>
			api.post('/v1/auth/login/', credentials),
		refresh: (refreshToken: string) =>
			api.post('/v1/auth/refresh/', { refresh_token: refreshToken }),
		logout: (data: { refresh_token: string, username?: string }) => api.post('/v1/auth/logout', data, { keepalive: true }),
		me: () => api.get('/v1/auth/me/'),
		health: () => api.get('/health')
	},

	tenants: {
		list: (page = 1, pageSize = 50) =>
			api.get(`/v1/tenants/?page=${page}&page_size=${pageSize}`),
		get: (id: number) => api.get(`/v1/tenants/${id}/`),
		create: (data: any) => api.post('/v1/tenants/', data),
		update: (id: number, data: any) => api.put(`/v1/tenants/${id}/`, data)
	},

	licenses: {
		get: (tenantId: number) => api.get(`/v1/licenses/tenant/${tenantId}/`),
		myLicense: () => api.get('/v1/licenses/my-license/'),
		usage: (tenantId: number) => api.get(`/v1/licenses/usage/${tenantId}/`),
		validate: (tenantId: number) => api.get(`/v1/licenses/validate/${tenantId}/`)
	},

	// CSV import for Operaciones de Importación/Exportación (facturas: encabezados y partidas).
	// Backend: api/v1/modules/a76/layouts_csv/facturas (rutas bajo /v1/a76/imports/).
	imports: {
		upload: (
			file: File,
			modelTarget: string,
			footerConfig: any,
			companyId: number,
			operationType: string,
			templateId?: string
		) => {
			const formData = new FormData();
			formData.append('file', file);
			if (footerConfig) {
				formData.append('footer_config', JSON.stringify(footerConfig));
			}
			if (templateId) {
				formData.append('template_id', templateId);
			}

			const queryParams = new URLSearchParams({
				company_id: String(companyId),
				operation_type: operationType || 'imp'
			}).toString();

			return fetchApi(`/v1/a76/imports/upload/${modelTarget}?${queryParams}`, {
				method: 'POST',
				body: formData
			});
		},
		status: (jobId: string) => api.get(`/v1/a76/imports/${jobId}/status`),
		commit: (jobId: string, modelTarget: string) =>
			api.post(`/v1/a76/imports/${jobId}/commit`, { model_target: modelTarget }),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Operaciones de Exportación (encabezado y partidas).
	// Backend: layouts_csv/exportacion — rutas /v1/a76/imports/exportacion/
	exportacionImports: {
		upload: (
			file: File,
			modelTarget: string,
			footerConfig: any,
			companyId: number,
			templateId?: string
		) => {
			const formData = new FormData();
			formData.append('file', file);
			if (footerConfig) {
				formData.append('footer_config', JSON.stringify(footerConfig));
			}
			if (templateId) {
				formData.append('template_id', templateId);
			}

			const queryParams = new URLSearchParams({
				company_id: String(companyId),
				operation_type: 'exp'
			}).toString();

			return fetchApi(`/v1/a76/imports/exportacion/upload/${modelTarget}?${queryParams}`, {
				method: 'POST',
				body: formData
			});
		},
		status: (jobId: string) => api.get(`/v1/a76/imports/exportacion/${jobId}/status`),
		commit: (jobId: string, modelTarget: string) =>
			api.post(`/v1/a76/imports/exportacion/${jobId}/commit`, { model_target: modelTarget }),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/imports/exportacion/${jobId}/errors/scan-csv`)
	},

	// CSV import for Agentes Aduanales (flujo propio en customs_brokers/imports)
	customsBrokerImports: {
		upload: (file: File, companyId: number) => {
			const formData = new FormData();
			formData.append('file', file);
			return fetchApi(
				`/v1/a76/customs-brokers/imports/upload?company_id=${companyId}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/customs-brokers/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/customs-brokers/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/customs-brokers/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Clientes y Proveedores (flujo propio en clients_and_providers/imports)
	clientProviderImports: {
		upload: (file: File, companyId: number) => {
			const formData = new FormData();
			formData.append('file', file);
			return fetchApi(
				`/v1/a76/clients-providers/imports/upload?company_id=${companyId}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/clients-providers/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/clients-providers/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/clients-providers/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Tipos de Cambio (flujo en general_catalogs/exchange_rate/imports)
	exchangeRateImports: {
		upload: (
			file: File,
			companyId: number,
			params?: { reemplazar_sin_preguntar?: boolean; date_format?: string }
		) => {
			const formData = new FormData();
			formData.append('file', file);
			const search = new URLSearchParams({ company_id: String(companyId) });
			if (params?.reemplazar_sin_preguntar !== undefined)
				search.set('reemplazar_sin_preguntar', String(!!params.reemplazar_sin_preguntar));
			if (params?.date_format != null && params.date_format !== '')
				search.set('date_format', params.date_format);
			return fetchApi(
				`/v1/a76/exchange-rate/imports/upload?${search.toString()}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/exchange-rate/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/exchange-rate/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/exchange-rate/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Fracción Americana (us_tariff_fractions/imports)
	americanFractionImports: {
		upload: (file: File, companyId: number) => {
			const formData = new FormData();
			formData.append('file', file);
			return fetchApi(
				`/v1/a76/us-tariff-fractions/imports/upload?company_id=${companyId}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/us-tariff-fractions/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/us-tariff-fractions/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/us-tariff-fractions/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Pedimentos (pedimentos/imports)
	pedimentosImports: {
		upload: (
			file: File,
			companyId: number,
			params?: { actualizar?: boolean; dateFormat?: string }
		) => {
			const formData = new FormData();
			formData.append('file', file);
			const search = new URLSearchParams({ company_id: String(companyId) });
			if (params?.actualizar !== undefined) search.set('actualizar', String(!!params.actualizar));
			if (params?.dateFormat != null) search.set('dateFormat', params.dateFormat);
			return fetchApi(
				`/v1/a76/pedimentos/imports/upload?${search.toString()}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/pedimentos/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/pedimentos/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/pedimentos/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Clases de Materiales (classes/imports)
	materialClassImports: {
		upload: (
			file: File,
			companyId: number,
			params?: { actualizar?: boolean; siempre_toda?: boolean }
		) => {
			const formData = new FormData();
			formData.append('file', file);
			const search = new URLSearchParams({ company_id: String(companyId) });
			if (params?.actualizar !== undefined) search.set('actualizar', String(!!params.actualizar));
			if (params?.siempre_toda !== undefined) search.set('siempre_toda', String(!!params.siempre_toda));
			return fetchApi(
				`/v1/a76/classes/imports/upload?${search.toString()}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/classes/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/classes/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/classes/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Vehículos / Transportes (transportation/vehicles/imports)
	vehicleImports: {
		upload: (file: File, companyId: number, options?: { actualizar?: boolean }) => {
			const formData = new FormData();
			formData.append('file', file);
			const params = new URLSearchParams({ company_id: String(companyId) });
			if (options?.actualizar !== undefined) params.set('actualizar', String(options.actualizar));
			return fetchApi(
				`/v1/a76/transportation/vehicles/imports/upload?${params.toString()}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/transportation/vehicles/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/transportation/vehicles/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/transportation/vehicles/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Conductores (drivers/imports)
	driverImports: {
		upload: (file: File, companyId: number) => {
			const formData = new FormData();
			formData.append('file', file);
			return fetchApi(
				`/v1/a76/drivers/imports/upload?company_id=${companyId}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/drivers/imports/${jobId}/status`),
		commit: (jobId: string) => api.post(`/v1/a76/drivers/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/drivers/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Trailers y Cajas (transportation/trailers/imports)
	trailerImports: {
		upload: (file: File, companyId: number, params?: { actualizar?: boolean }) => {
			const formData = new FormData();
			formData.append('file', file);
			const actualizar = params?.actualizar ?? false;
			return fetchApi(
				`/v1/a76/transportation/trailers/imports/upload?company_id=${companyId}&actualizar=${actualizar}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/transportation/trailers/imports/${jobId}/status`),
		commit: (jobId: string) =>
			api.post(`/v1/a76/transportation/trailers/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/transportation/trailers/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Transportistas (transporters/imports)
	transporterImports: {
		upload: (file: File, companyId: number, params?: { actualizar?: boolean }) => {
			const formData = new FormData();
			formData.append('file', file);
			const actualizar = params?.actualizar ?? false;
			return fetchApi(
				`/v1/a76/transporters/imports/upload?company_id=${companyId}&actualizar=${actualizar}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/transporters/imports/${jobId}/status`),
		commit: (jobId: string) => api.post(`/v1/a76/transporters/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/transporters/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for Números de parte (parts/imports)
	partNumberImports: {
		upload: (file: File, companyId: number, options?: { actualizar?: boolean; reemplazar_sin_preguntar?: boolean }) => {
			const formData = new FormData();
			formData.append('file', file);
			const params = new URLSearchParams({ company_id: String(companyId) });
			if (options?.actualizar !== undefined) params.set('actualizar', String(options.actualizar));
			if (options?.reemplazar_sin_preguntar !== undefined) params.set('reemplazar_sin_preguntar', String(options.reemplazar_sin_preguntar));
			return fetchApi(
				`/v1/a76/parts/imports/upload?${params.toString()}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/parts/imports/${jobId}/status`),
		commit: (jobId: string) => api.post(`/v1/a76/parts/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) =>
			fetchBlob(`/v1/a76/parts/imports/${jobId}/errors/scan-csv`)
	},

	// CSV import for BOMs (boms/imports)
	bomImports: {
		upload: (file: File, companyId: number) => {
			const formData = new FormData();
			formData.append('file', file);
			return fetchApi(
				`/v1/a76/boms/imports/upload?company_id=${companyId}`,
				{ method: 'POST', body: formData }
			);
		},
		status: (jobId: string) => api.get(`/v1/a76/boms/imports/${jobId}/status`),
		commit: (jobId: string) => api.post(`/v1/a76/boms/imports/${jobId}/commit`, {}),
		downloadScanErrorsCsv: (jobId: string) => fetchBlob(`/v1/a76/boms/imports/${jobId}/errors/scan-csv`)
	},

	// Generic request for custom needs (like file uploads)
	request: <T = any>(endpoint: string, options: RequestInit = {}) => fetchApi<T>(endpoint, options)
};
