import React, { createContext, useState, useContext, type ReactNode, useEffect } from 'react';
import type { LoginResponseDto } from '../models/dtos/Usuarios/LoginResponseDto';
import { jwtDecode } from 'jwt-decode';

// Interfaz para los datos del usuario que guardaremos en el contexto
export interface UserContextData {
  userId: number;
  username: string;
  nombreCompleto: string;
  esSuperAdmin: boolean;
  debeCambiarClave: boolean;
  perfil: string;
  idPerfil: number;
  permissions: string[]; // Guardamos los codAcc
}

// Interfaz para el payload decodificado del JWT
interface DecodedJwtPayload {
  sub: string;          // User ID (viene como string)
  name: string;         // Username
  given_name?: string;  // Nombre (estándar, pero verifica tu token)
  family_name?: string; // Apellido (estándar, pero verifica tu token)
  role: string | string[]; // Puede ser uno o varios roles
  perfil: string;
  idPerfil: string;     // (viene como string)
  debeCambiarClave: string; // (viene como string "True" o "False")
  permission?: string | string[]; // Nuestros claims de permiso (codAcc)
  [key: string]: any;   // Para otros claims
}

interface AuthContextType {
  isAuthenticated: boolean;
  user: UserContextData | null; // Usar el tipo extendido
  token: string | null;
  isLoading: boolean;
  showForcedPasswordChangeModal: boolean;
  isPasswordChangeForced: boolean;
  setShowForcedPasswordChangeModal: (show: boolean) => void;
  passwordChangeCompleted: () => void;
  login: (apiLoginResponse: LoginResponseDto) => void; // Recibe el DTO de la API
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
  const [user, setUser] = useState<UserContextData | null>(null);
  const [token, setToken] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [showForcedPasswordChangeModal, setShowForcedPasswordChangeModal] = useState<boolean>(false);
  const [isPasswordChangeForced, setIsPasswordChangeForced] = useState<boolean>(false);

  const processTokenAndSetUser = (jwtToken: string) => {
    try {
      const decodedToken = jwtDecode<DecodedJwtPayload>(jwtToken);

      // Verificar expiración (opcional, pero buena práctica aquí también)
      const currentTime = Date.now() / 1000;
      if (decodedToken.exp && decodedToken.exp < currentTime) {
        console.warn("Token expirado al procesar.");
        logout(); // Llama a logout que limpia todo
        return;
      }

      let permissions: string[] = [];
      if (decodedToken.permission) {
        permissions = Array.isArray(decodedToken.permission) ? decodedToken.permission : [decodedToken.permission];
      }

      const userForContext: UserContextData = {
        userId: parseInt(decodedToken.sub, 10),
        username: decodedToken.name,
        nombreCompleto: `${decodedToken.given_name || ''} ${decodedToken.family_name || ''}`.trim(),
        esSuperAdmin: decodedToken.role === "SuperAdmin" || (Array.isArray(decodedToken.role) && decodedToken.role.includes("SuperAdmin")),
        debeCambiarClave: decodedToken.debeCambiarClave?.toLowerCase() === 'true',
        idPerfil: decodedToken.idPerfil ? parseInt(decodedToken.idPerfil, 10) : 0,
        permissions: permissions,
        perfil: decodedToken.perfil || 'Usuario' // Asignar un valor por defecto si no existe
      };

      setToken(jwtToken);
      setUser(userForContext);
      setIsAuthenticated(true);
      localStorage.setItem('authToken', jwtToken);
      localStorage.setItem('authUser', JSON.stringify(userForContext)); // Guardar el usuario procesado

      // Lógica para el modal de cambio de clave
      if (userForContext.debeCambiarClave) {
        setShowForcedPasswordChangeModal(true);
        setIsPasswordChangeForced(true);
      }

    } catch (error) {
      console.error("Error al decodificar o procesar token:", error);
      logout(); // Limpiar estado si el token es inválido
    }
  };

  useEffect(() => {
    setIsLoading(true);
    const storedToken = localStorage.getItem('authToken');
    if (storedToken) {
      processTokenAndSetUser(storedToken);
    }
    setIsLoading(false);
  }, []);

  const login = (apiLoginResponse: LoginResponseDto) => {
    processTokenAndSetUser(apiLoginResponse.token); // Procesar el token recibido
  };

  const logout = () => {
    localStorage.removeItem('authToken');
    localStorage.removeItem('authUser');
    setToken(null);
    setUser(null);
    setIsAuthenticated(false);
    setShowForcedPasswordChangeModal(false);
    setIsPasswordChangeForced(false);
  };

  const passwordChangeCompleted = () => {
    setShowForcedPasswordChangeModal(false);
    setIsPasswordChangeForced(false);
    // Importante: Si el cambio de clave afecta el claim "debeCambiarClave" en el token,
    // idealmente el backend debería devolver un *nuevo token* después del cambio de clave.
    // Si no lo hace, el token actual aún dirá que debe cambiar clave.
    // Una solución simple es actualizar el estado local del usuario:
    if (user) {
      const updatedUser = { ...user, debeCambiarClave: false };
      setUser(updatedUser);
      localStorage.setItem('authUser', JSON.stringify(updatedUser));
    }
  };

  return (
    <AuthContext.Provider value={{
      isAuthenticated, user, token, isLoading,
      showForcedPasswordChangeModal, isPasswordChangeForced,
      setShowForcedPasswordChangeModal, passwordChangeCompleted,
      login, logout
    }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = (): AuthContextType => {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};