/**
 * Sesión: almacenamiento seguro del token (SecureStore), usuario actual y
 * acciones login/logout. Registra el cliente HTTP para que inyecte el token y
 * reaccione a un 401 (token caducado → re-login).
 */
import * as SecureStore from 'expo-secure-store';
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { configureClient } from '../api/client';
import * as api from '../api/endpoints';
import { User } from '../api/types';
import { APP_VERSION } from '../config';
import { wipeDatabase } from '../db/database';

const TOKEN_KEY = 'auth_token';
const USER_KEY = 'auth_user';

interface SessionState {
  ready: boolean;
  token: string | null;
  user: User | null;
  signIn: (email: string, password: string, deviceName: string) => Promise<void>;
  signOut: () => Promise<void>;
}

const SessionContext = createContext<SessionState | null>(null);

export function SessionProvider({ children }: { children: React.ReactNode }) {
  const [ready, setReady] = useState(false);
  const [token, setToken] = useState<string | null>(null);
  const [user, setUser] = useState<User | null>(null);

  // Ref para que el getToken del cliente HTTP siempre lea el valor vigente.
  const tokenRef = useRef<string | null>(null);
  tokenRef.current = token;

  const clear = useCallback(async () => {
    setToken(null);
    setUser(null);
    await SecureStore.deleteItemAsync(TOKEN_KEY);
    await SecureStore.deleteItemAsync(USER_KEY);
  }, []);

  // Configura el cliente HTTP una sola vez.
  useEffect(() => {
    configureClient({
      getToken: () => tokenRef.current,
      onUnauthorized: () => {
        // Token inválido/caducado: limpiamos sesión (la UI llevará al login).
        void clear();
      },
    });
  }, [clear]);

  // Rehidrata la sesión al arrancar.
  useEffect(() => {
    (async () => {
      try {
        const [t, u] = await Promise.all([
          SecureStore.getItemAsync(TOKEN_KEY),
          SecureStore.getItemAsync(USER_KEY),
        ]);
        if (t) {
          tokenRef.current = t;
          setToken(t);
          setUser(u ? (JSON.parse(u) as User) : null);
        }
      } finally {
        setReady(true);
      }
    })();
  }, []);

  const signIn = useCallback(
    async (email: string, password: string, deviceName: string) => {
      const res = await api.login({
        email,
        password,
        device_name: deviceName,
        app_version: APP_VERSION,
      });
      tokenRef.current = res.token;
      setToken(res.token);
      setUser(res.user);
      await SecureStore.setItemAsync(TOKEN_KEY, res.token);
      await SecureStore.setItemAsync(USER_KEY, JSON.stringify(res.user));
    },
    [],
  );

  const signOut = useCallback(async () => {
    try {
      await api.logout();
    } catch {
      // Aunque falle la revocación remota, limpiamos local.
    }
    await clear();
    await wipeDatabase();
  }, [clear]);

  const value = useMemo<SessionState>(
    () => ({ ready, token, user, signIn, signOut }),
    [ready, token, user, signIn, signOut],
  );

  return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
}

export function useSession(): SessionState {
  const ctx = useContext(SessionContext);
  if (!ctx) throw new Error('useSession debe usarse dentro de <SessionProvider>');
  return ctx;
}

export function hasPermission(user: User | null, permission: string): boolean {
  return Boolean(user?.permissions?.includes(permission));
}
