/**
 * Servicio de autenticación con Keycloak
 *
 * Seguridad de tokens:
 *  - access_token  → en memoria (authStore) + cookie no-HttpOnly (para SSR)
 *  - refresh_token → cookie HttpOnly únicamente (JS nunca lo lee directamente)
 *  - El refresh se hace server-side via /api-sveltekit/auth/silent-refresh
 *  - NO se usa localStorage para tokens
 */

import Keycloak from 'keycloak-js';
import { writable, derived } from 'svelte/store';
import { browser } from '$app/environment';

// ─────────────────────────────────────────────────────────
// Tipos
// ─────────────────────────────────────────────────────────

export interface User {
	id: string;
	username: string;
	email?: string;
	name?: string;
	tenantId?: number;
	roles: string[];
}

export interface AuthState {
	isAuthenticated: boolean;
	isLoading: boolean;
	user: User | null;
	token: string | null;
}

// ─────────────────────────────────────────────────────────
// Configuración de Keycloak
// ─────────────────────────────────────────────────────────

const keycloakConfig = {
	url: import.meta.env.VITE_KEYCLOAK_URL,
	realm: import.meta.env.VITE_KEYCLOAK_REALM,
	clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID
};

let keycloakInstance: Keycloak | null = null;

// ─────────────────────────────────────────────────────────
// Cookie helpers (solo para access_token no-HttpOnly)
// ─────────────────────────────────────────────────────────

/** Lee el valor de una cookie no-HttpOnly */
const getCookie = (name: string): string | null => {
	if (!browser) return null;
	const value = `; ${document.cookie}`;
	const parts = value.split(`; ${name}=`);
	if (parts.length === 2) return parts.pop()?.split(';').shift() ?? null;
	return null;
};

/** Escribe una cookie no-HttpOnly */
const setCookie = (name: string, value: string, days: number = 7) => {
	if (!browser) return;
	const exp = new Date();
	exp.setDate(exp.getDate() + days);
	const secure = window.location.protocol === 'https:' ? '; Secure' : '';
	document.cookie = `${name}=${value}; path=/; expires=${exp.toUTCString()}; SameSite=Lax${secure}`;
};

/** Elimina una cookie */
const deleteCookie = (name: string) => {
	if (!browser) return;
	const secure = window.location.protocol === 'https:' ? '; Secure' : '';
	document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; SameSite=Lax${secure}`;
};

// ─────────────────────────────────────────────────────────
// Auth store (tokens solo en memoria)
// ─────────────────────────────────────────────────────────

const createAuthStore = () => {
	const { subscribe, set, update } = writable<AuthState>({
		isAuthenticated: false,
		isLoading: true,
		user: null,
		token: null
	});

	return {
		subscribe,
		setAuthenticated: (authenticated: boolean) =>
			update((s) => ({ ...s, isAuthenticated: authenticated })),
		setLoading: (loading: boolean) =>
			update((s) => ({ ...s, isLoading: loading })),
		setUser: (user: User | null) => update((s) => ({ ...s, user })),
		setToken: (token: string | null) => update((s) => ({ ...s, token })),
		/** ⚠️  Los tokens ya NO se guardan en localStorage; solo en memoria. */
		setTokens: (accessToken: string, _refreshToken?: string) => {
			update((s) => ({ ...s, token: accessToken }));
			// El refresh_token llega en cookie HttpOnly desde el servidor;
			// el cliente no lo almacena ni lo lee en ningún momento.
		},
		reset: () =>
			set({
				isAuthenticated: false,
				isLoading: false,
				user: null,
				token: null
			})
	};
};

export const authStore = createAuthStore();

export const isAuthenticated = derived(authStore, ($a) => $a.isAuthenticated);
export const currentUser = derived(authStore, ($a) => $a.user);

// ─────────────────────────────────────────────────────────
// Inicialización
// ─────────────────────────────────────────────────────────

/**
 * Inicializa el estado de autenticación en el cliente.
 * - Si hay un access_token en la cookie no-HttpOnly, lo usa.
 * - En cualquier caso intenta inicializar Keycloak JS (para el flujo SSO).
 */
export const initAuth = async (): Promise<boolean> => {
	if (!browser) return false;

	try {
		authStore.setLoading(true);

		// Restaurar token desde cookie no-HttpOnly (password login flow)
		const cookieToken = getCookie('access_token');
		if (cookieToken) {
			authStore.setToken(cookieToken);
			authStore.setAuthenticated(true);
			await loadUserInfo(cookieToken).catch(() => {});
			authStore.setLoading(false);
			return true;
		}

		// Sin token local, intentar Keycloak JS (flujo SSO)
		const authenticated = await initKeycloak();
		authStore.setLoading(false);
		return authenticated;
	} catch (err) {
		console.error('[auth] Error en initAuth:', err);
		authStore.setLoading(false);
		return false;
	}
};

/** Inicializa Keycloak JS para el flujo SSO con PKCE */
export const initKeycloak = async (): Promise<boolean> => {
	if (!browser) return false;

	try {
		keycloakInstance = new Keycloak(keycloakConfig);

		const authenticated = await keycloakInstance.init({
			onLoad: 'check-sso',
			silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
			pkceMethod: 'S256',
			checkLoginIframe: false
		});

		if (authenticated) {
			await updateAuthState();
			setupKeycloakTokenHooks();
		}

		return authenticated;
	} catch (err) {
		console.error('[auth] Error inicializando Keycloak:', err);
		return false;
	}
};

let previousTenantId: number | undefined = undefined;

const updateAuthState = async () => {
	if (!keycloakInstance?.authenticated) {
		authStore.reset();
		return;
	}

	try {
		const profile = await keycloakInstance.loadUserProfile();
		const token = keycloakInstance.token ?? null;
		const parsed = keycloakInstance.tokenParsed as any;

		const roles: string[] = parsed?.realm_access?.roles ?? [];
		const tenantId: number | undefined = parsed?.tenant_id
			? parseInt(parsed.tenant_id)
			: undefined;

		const tenantChanged = previousTenantId !== undefined && previousTenantId !== tenantId;

		const user: User = {
			id: profile.id ?? '',
			username: profile.username ?? '',
			email: profile.email,
			name: `${profile.firstName ?? ''} ${profile.lastName ?? ''}`.trim(),
			tenantId,
			roles
		};

		authStore.setAuthenticated(true);
		authStore.setUser(user);
		authStore.setToken(token);

		if (tenantChanged && browser) {
			try {
				const { companyStore } = await import('./stores/company.svelte');
				companyStore.clear();
			} catch {}
		}

		previousTenantId = tenantId;
	} catch (err) {
		console.error('[auth] Error actualizando estado:', err);
		authStore.reset();
	}
};

// ─────────────────────────────────────────────────────────
// Keycloak JS token hooks (solo para el flujo SSO)
// ─────────────────────────────────────────────────────────

/**
 * Configura los callbacks de Keycloak JS para notificar al SessionManager
 * sobre cambios de token y eventos de sesión SSO.
 */
const setupKeycloakTokenHooks = () => {
	if (!keycloakInstance) return;

	keycloakInstance.onTokenExpired = () => {
		keycloakInstance
			?.updateToken(70)
			.then((refreshed) => {
				if (refreshed && keycloakInstance?.token) {
					authStore.setToken(keycloakInstance.token);
					import('./session-manager')
						.then(({ getSessionManager }) => {
							getSessionManager()?.updateToken(keycloakInstance!.token!);
						})
						.catch(() => {});
				}
			})
			.catch(() => {
				console.error('[auth] No se pudo refrescar el token de Keycloak');
				void logout();
			});
	};

	keycloakInstance.onAuthRefreshSuccess = () => {
		if (keycloakInstance?.token) authStore.setToken(keycloakInstance.token);
	};

	keycloakInstance.onAuthRefreshError = () => {
		console.error('[auth] Error en refresh de Keycloak — cerrando sesión');
		void logout();
	};

	keycloakInstance.onAuthLogout = () => {
		authStore.reset();
	};
};

// ─────────────────────────────────────────────────────────
// Login
// ─────────────────────────────────────────────────────────

/** Inicia sesión con Keycloak (OAuth redirect flow) */
export const loginWithKeycloak = async (tenantSlug?: string) => {
	if (!keycloakInstance) {
		console.error('[auth] Keycloak no está inicializado');
		return;
	}
	const options: any = { redirectUri: window.location.origin + '/callback' };
	if (tenantSlug) options.loginHint = tenantSlug;
	await keycloakInstance.login(options);
};

/**
 * Login con usuario/contraseña (legacy — el login principal es via form action del servidor).
 * Los tokens se guardan en cookies (vía setCookie) y en memoria (authStore).
 * NO se guardan en localStorage.
 */
export const login = async (credentials: {
	username: string;
	password: string;
	tenant_slug: string;
}): Promise<{ success: boolean; error?: string; data?: any }> => {
	try {
		const { api } = await import('./api');
		const response = await api.auth.login(credentials);

		if (response.error) {
			return { success: false, error: response.error };
		}

		const loginData = response.data;
		if (loginData?.access_token) {
			// Guardar en memoria y en cookie no-HttpOnly para SSR
			authStore.setToken(loginData.access_token);
			authStore.setAuthenticated(true);
			setCookie('access_token', loginData.access_token);
			// El refresh_token llega en cookie HttpOnly desde el servidor.
			// NO lo guardamos en JS.
			await loadUserInfo(loginData.access_token);
		}

		return { success: true, data: loginData };
	} catch (err) {
		console.error('[auth] Error en login:', err);
		return { success: false, error: 'Error de conexión con el servidor' };
	}
};

// ─────────────────────────────────────────────────────────
// User info
// ─────────────────────────────────────────────────────────

const loadUserInfo = async (token: string) => {
	try {
		authStore.setToken(token);
		const { api } = await import('./api');
		const response = await api.auth.me();
		if (response.data) {
			const d = response.data;
			authStore.setUser({
				id: d.sub ?? '',
				username: d.preferred_username ?? d.username ?? '',
				email: d.email,
				name: d.name,
				tenantId: d.tenant_id,
				roles: d.realm_access?.roles ?? []
			});
		}
	} catch (err) {
		console.error('[auth] Error cargando info del usuario:', err);
	}
};

// ─────────────────────────────────────────────────────────
// Logout
// ─────────────────────────────────────────────────────────

export const logout = async () => {
	if (!browser) return;

	try {
		// Detener el SessionManager
		try {
			const { destroySessionManager } = await import('./session-manager');
			destroySessionManager();
		} catch {}

		// Limpiar store de compañías
		try {
			const { companyStore } = await import('./stores/company.svelte');
			companyStore.clear();
		} catch {}

		// Limpiar estado en memoria
		authStore.reset();

		// Eliminar cookie no-HttpOnly del access_token
		deleteCookie('access_token');
		// La cookie HttpOnly del refresh_token la limpia el servidor

		// Logout de Keycloak JS si estaba autenticado con SSO
		if (keycloakInstance?.authenticated) {
			try {
				await fetch('/logout', { method: 'POST' });
			} catch {}
			await keycloakInstance.logout({
				redirectUri: window.location.origin + '/login'
			});
			return;
		}

		// Para login con password: POST al logout route del servidor
		const form = document.createElement('form');
		form.method = 'POST';
		form.action = '/logout';
		document.body.appendChild(form);
		form.submit();
	} catch (err) {
		console.error('[auth] Error durante logout:', err);
		window.location.href = '/login';
	}
};

// ─────────────────────────────────────────────────────────
// Token accessors
// ─────────────────────────────────────────────────────────

export const hasRole = (role: string): boolean => {
	if (!keycloakInstance?.authenticated) return false;
	return keycloakInstance.hasRealmRole(role);
};

/** Obtiene el access token desde memoria (Keycloak JS o authStore) */
export const getToken = (): string | null => {
	// Prioridad 1: Keycloak JS en memoria
	if (keycloakInstance?.token) return keycloakInstance.token;

	// Prioridad 2: authStore en memoria
	let token: string | null = null;
	const unsub = authStore.subscribe((s) => { token = s.token; });
	unsub();
	if (token) return token;

	// Prioridad 3: cookie no-HttpOnly (fallback para acceso inicial antes del onMount)
	if (browser) return getCookie('access_token');

	return null;
};

/**
 * Refresca el access token usando el endpoint server-side seguro.
 * El servidor lee el refresh_token de la cookie HttpOnly.
 * @returns true si el refresh fue exitoso
 */
export const refreshAccessToken = async (): Promise<boolean> => {
	if (!browser) return false;

	// Con Keycloak JS activo, usar su mecanismo nativo
	if (keycloakInstance?.authenticated) {
		try {
			const refreshed = await keycloakInstance.updateToken(70);
			if (refreshed || keycloakInstance.token) {
				authStore.setToken(keycloakInstance.token ?? null);
				return true;
			}
		} catch {
			await logout();
			return false;
		}
	}

	// Flujo de contraseña: usar el endpoint server-side seguro
	try {
		const resp = await fetch('/api-sveltekit/auth/silent-refresh', {
			method: 'POST',
			credentials: 'include',
			headers: { 'Content-Type': 'application/json' }
		});

		if (!resp.ok) {
			await logout();
			return false;
		}

		const data = await resp.json() as { access_token?: string };
		if (data.access_token) {
			authStore.setToken(data.access_token);
			setCookie('access_token', data.access_token);
			return true;
		}
	} catch (err) {
		console.error('[auth] Error en refreshAccessToken:', err);
	}

	await logout();
	return false;
};

/** @deprecated El refresh_token ya no se expone en JS. */
export const getRefreshToken = (): string | null => {
	console.warn('[auth] getRefreshToken() está deprecado — el refresh_token no se expone en JS.');
	return null;
};

export const getKeycloakInstance = (): Keycloak | null => keycloakInstance;
