/**
 * 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 = [];
}

/**
 * Intenta refrescar el token usando el refresh token
 */
async function refreshToken(): Promise<string | null> {
	if (!browser) return null;

	let refreshTokenValue = localStorage.getItem('refresh_token');

	// Si no está en localStorage, intentar obtenerlo de las cookies
	if (!refreshTokenValue) {
		const getCookie = (name: string): string | null => {
			const value = `; ${document.cookie}`;
			const parts = value.split(`; ${name}=`);
			if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
			return null;
		};

		refreshTokenValue = getCookie('refresh_token');
		if (refreshTokenValue) {
			localStorage.setItem('refresh_token', refreshTokenValue);
		}
	}

	if (!refreshTokenValue) {
		console.error('❌ [API] No hay refresh token disponible');
		return null;
	}

	try {
		const response = await fetch(`${API_BASE_URL}/v1/auth/refresh`, {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json'
			},
			body: JSON.stringify({ refresh_token: refreshTokenValue }),
			credentials: 'include'
		});

		if (!response.ok) {
			console.error('❌ [API] Refresh token expirado o inválido, status:', response.status);
			// Si el refresh token también está expirado, limpiar todo
			localStorage.removeItem('access_token');
			localStorage.removeItem('refresh_token');
			// Limpiar cookies también
			document.cookie = 'access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC';
			document.cookie = 'refresh_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC';
			// Redirigir al login después de un pequeño delay para que el usuario vea el mensaje
			setTimeout(() => {
				if (browser) {
					window.location.href = '/login';
				}
			}, 2000);
			return null;
		}

		const data = await response.json();

		// Guardar los nuevos tokens
		if (data.access_token) {
			localStorage.setItem('access_token', data.access_token);

			if (data.refresh_token) {
				localStorage.setItem('refresh_token', data.refresh_token);
			}

			// Actualizar también las cookies
			const isSecure = window.location.protocol === 'https:';
			const secureFlag = isSecure ? '; Secure' : '';

			document.cookie = `access_token=${data.access_token}; path=/; max-age=${60 * 60 * 24 * 7}; SameSite=Lax${secureFlag}`;
			if (data.refresh_token) {
				document.cookie = `refresh_token=${data.refresh_token}; path=/; max-age=${60 * 60 * 24 * 30}; SameSite=Lax${secureFlag}`;
			}

			// Actualizar el authStore si está disponible
			try {
				const { authStore } = await import('./auth');
				authStore.setToken(data.access_token);
			} catch (e) {
				// Si no se puede importar authStore, no es crítico
				console.warn('⚠️ [API] No se pudo actualizar authStore:', e);
			}

			return data.access_token;
		}

		return null;
	} catch (error) {
		console.error('❌ [API] Error refreshing token:', 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 || 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
		};
	}
}

// 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 }),

	// 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}/`)
	},

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