package dev.lions.user.manager.client.view;

import dev.lions.user.manager.client.service.RealmAssignmentServiceClient;
import dev.lions.user.manager.client.service.RealmServiceClient;
import dev.lions.user.manager.client.service.UserServiceClient;
import dev.lions.user.manager.dto.realm.RealmAssignmentDTO;
import dev.lions.user.manager.dto.user.UserDTO;
import dev.lions.user.manager.dto.user.UserSearchResultDTO;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import lombok.Data;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * Bean JSF pour la gestion des affectations de realms
 * Permet d'assigner des realms aux utilisateurs pour le contrôle d'accès
 * multi-tenant
 *
 * @author Lions User Manager
 * @version 1.0.0
 */
@Named("realmAssignmentBean")
@ViewScoped
@Data
public class RealmAssignmentBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOGGER = Logger.getLogger(RealmAssignmentBean.class.getName());

    @Inject
    @RestClient
    private RealmAssignmentServiceClient realmAssignmentServiceClient;

    @Inject
    @RestClient
    private UserServiceClient userServiceClient;

    @Inject
    @RestClient
    private RealmServiceClient realmServiceClient;

    @Inject
    private UserSessionBean userSessionBean;

    // Listes
    private List<RealmAssignmentDTO> assignments = new ArrayList<>();
    private List<UserDTO> availableUsers = new ArrayList<>();
    private List<String> availableRealms = new ArrayList<>();
    private RealmAssignmentDTO selectedAssignment;

    // Pour la création/édition
    private RealmAssignmentDTO newAssignment = RealmAssignmentDTO.builder()
            .active(true)
            .temporaire(false)
            .build();
    private String selectedUserId;
    private String selectedRealmName;

    // Filtres
    private String filterUserName;
    private String filterRealmName;

    @PostConstruct
    public void init() {
        LOGGER.info("Initialisation de RealmAssignmentBean");

        // Vérifier si l'utilisateur est admin
        if (!userSessionBean.hasRole("admin")) {
            addErrorMessage("Accès refusé: Cette fonctionnalité est réservée aux administrateurs");
            return;
        }

        loadAssignments();
        loadAvailableUsers();
        loadAvailableRealms();
    }

    /**
     * Charger toutes les affectations
     */
    public void loadAssignments() {
        try {
            LOGGER.info("Chargement de toutes les affectations de realms");
            assignments = realmAssignmentServiceClient.getAllAssignments();
            LOGGER.info("Chargement réussi: " + assignments.size() + " affectation(s) trouvée(s)");

            if (assignments.isEmpty()) {
                addInfoMessage("Aucune affectation de realm configurée");
            }
        } catch (Exception e) {
            String errorMsg = "Erreur lors du chargement des affectations: " + e.getMessage();
            LOGGER.severe(errorMsg);
            LOGGER.log(java.util.logging.Level.SEVERE, "Exception complète", e);
            addErrorMessage(errorMsg);
            assignments = new ArrayList<>();
        }
    }

    /**
     * Charger les utilisateurs disponibles
     */
    public void loadAvailableUsers() {
        try {
            LOGGER.info("Chargement des utilisateurs disponibles");
            // Charger les utilisateurs du realm lions-user-manager (page 0, 100
            // utilisateurs max)
            UserSearchResultDTO result = userServiceClient.getAllUsers("lions-user-manager", 0, 100);
            availableUsers = result != null && result.getUsers() != null ? result.getUsers() : new ArrayList<>();
            LOGGER.info("Chargement réussi: " + availableUsers.size() + " utilisateur(s) disponible(s)");
        } catch (Exception e) {
            String errorMsg = "Erreur lors du chargement des utilisateurs: " + e.getMessage();
            LOGGER.severe(errorMsg);
            addErrorMessage(errorMsg);
            availableUsers = new ArrayList<>();
        }
    }

    /**
     * Charger les realms disponibles depuis Keycloak
     */
    public void loadAvailableRealms() {
        try {
            LOGGER.info("Chargement des realms disponibles depuis Keycloak");
            List<String> realms = realmServiceClient.getAllRealms();

            if (realms == null || realms.isEmpty()) {
                LOGGER.warning("Aucun realm trouvé dans Keycloak");
                availableRealms = Collections.emptyList();
                addInfoMessage("Aucun realm disponible dans Keycloak");
            } else {
                availableRealms = new ArrayList<>(realms);
                LOGGER.info("Realms disponibles chargés depuis Keycloak: " + availableRealms.size());
            }
        } catch (Exception e) {
            String errorMsg = "Erreur lors du chargement des realms depuis Keycloak: " + e.getMessage();
            LOGGER.severe(errorMsg);
            LOGGER.log(java.util.logging.Level.SEVERE, "Exception complète", e);
            addErrorMessage(errorMsg);
            // Fallback: liste vide plutôt que des données fictives
            availableRealms = Collections.emptyList();
        }
    }

    /**
     * Assigner un realm à un utilisateur
     */
    public void assignRealm() {
        try {
            if (selectedUserId == null || selectedUserId.isEmpty()) {
                addErrorMessage("Veuillez sélectionner un utilisateur");
                return;
            }

            if (selectedRealmName == null || selectedRealmName.isEmpty()) {
                addErrorMessage("Veuillez sélectionner un realm");
                return;
            }

            // Trouver l'utilisateur sélectionné
            UserDTO selectedUser = availableUsers.stream()
                    .filter(u -> u.getId().equals(selectedUserId))
                    .findFirst()
                    .orElse(null);

            if (selectedUser == null) {
                addErrorMessage("Utilisateur introuvable");
                return;
            }

            // Construire l'assignation
            RealmAssignmentDTO assignment = RealmAssignmentDTO.builder()
                    .userId(selectedUserId)
                    .username(selectedUser.getUsername())
                    .email(selectedUser.getEmail())
                    .realmName(selectedRealmName)
                    .isSuperAdmin(false)
                    .assignedAt(LocalDateTime.now())
                    .assignedBy(userSessionBean.getUsername())
                    .raison(newAssignment.getRaison())
                    .commentaires(newAssignment.getCommentaires())
                    .temporaire(newAssignment.getTemporaire() != null && newAssignment.getTemporaire())
                    .dateExpiration(newAssignment.getDateExpiration())
                    .active(true)
                    .build();

            LOGGER.info("Assignation du realm " + selectedRealmName + " à l'utilisateur " + selectedUser.getUsername());

            jakarta.ws.rs.core.Response response = realmAssignmentServiceClient.assignRealmToUser(assignment);
            response.readEntity(RealmAssignmentDTO.class);

            addSuccessMessage("Realm '" + selectedRealmName + "' assigné avec succès à " + selectedUser.getUsername());
            resetForm();
            loadAssignments();

        } catch (Exception e) {
            String errorMsg = "Erreur lors de l'assignation: " + e.getMessage();
            LOGGER.severe(errorMsg);
            LOGGER.log(java.util.logging.Level.SEVERE, "Exception complète", e);
            addErrorMessage(errorMsg);
        }
    }

    /**
     * Révoquer l'accès d'un utilisateur à un realm
     */
    public void revokeAssignment(RealmAssignmentDTO assignment) {
        try {
            if (assignment == null) {
                addErrorMessage("Assignation invalide");
                return;
            }

            LOGGER.info("Révocation du realm " + assignment.getRealmName() + " pour l'utilisateur "
                    + assignment.getUsername());

            realmAssignmentServiceClient.revokeRealmFromUser(assignment.getUserId(), assignment.getRealmName());

            addSuccessMessage(
                    "Accès révoqué pour " + assignment.getUsername() + " au realm '" + assignment.getRealmName() + "'");
            loadAssignments();

        } catch (Exception e) {
            String errorMsg = "Erreur lors de la révocation: " + e.getMessage();
            LOGGER.severe(errorMsg);
            LOGGER.log(java.util.logging.Level.SEVERE, "Exception complète", e);
            addErrorMessage(errorMsg);
        }
    }

    /**
     * Désactiver une assignation
     */
    public void deactivateAssignment(RealmAssignmentDTO assignment) {
        try {
            if (assignment == null || assignment.getId() == null) {
                addErrorMessage("Assignation invalide");
                return;
            }

            LOGGER.info("Désactivation de l'assignation " + assignment.getId());

            realmAssignmentServiceClient.deactivateAssignment(assignment.getId());

            addSuccessMessage("Assignation désactivée");
            loadAssignments();

        } catch (Exception e) {
            String errorMsg = "Erreur lors de la désactivation: " + e.getMessage();
            LOGGER.severe(errorMsg);
            addErrorMessage(errorMsg);
        }
    }

    /**
     * Activer une assignation
     */
    public void activateAssignment(RealmAssignmentDTO assignment) {
        try {
            if (assignment == null || assignment.getId() == null) {
                addErrorMessage("Assignation invalide");
                return;
            }

            LOGGER.info("Activation de l'assignation " + assignment.getId());

            realmAssignmentServiceClient.activateAssignment(assignment.getId());

            addSuccessMessage("Assignation activée");
            loadAssignments();

        } catch (Exception e) {
            String errorMsg = "Erreur lors de l'activation: " + e.getMessage();
            LOGGER.severe(errorMsg);
            addErrorMessage(errorMsg);
        }
    }

    /**
     * Définir un utilisateur comme super admin
     */
    public void setSuperAdmin(String userId, boolean superAdmin) {
        try {
            if (userId == null || userId.isEmpty()) {
                addErrorMessage("Utilisateur invalide");
                return;
            }

            UserDTO user = availableUsers.stream()
                    .filter(u -> u.getId().equals(userId))
                    .findFirst()
                    .orElse(null);

            String username = user != null ? user.getUsername() : userId;

            LOGGER.info("Définition de " + username + " comme super admin: " + superAdmin);

            realmAssignmentServiceClient.setSuperAdmin(userId, superAdmin);

            if (superAdmin) {
                addSuccessMessage(username + " est maintenant super admin (peut gérer tous les realms)");
            } else {
                addSuccessMessage("Privilèges super admin retirés pour " + username);
            }

            loadAssignments();

        } catch (Exception e) {
            String errorMsg = "Erreur lors de la modification du statut super admin: " + e.getMessage();
            LOGGER.severe(errorMsg);
            addErrorMessage(errorMsg);
        }
    }

    /**
     * Réinitialiser le formulaire
     */
    public void resetForm() {
        newAssignment = RealmAssignmentDTO.builder()
                .active(true)
                .temporaire(false)
                .build();
        selectedUserId = null;
        selectedRealmName = null;
    }

    /**
     * Obtenir les assignations filtrées
     */
    public List<RealmAssignmentDTO> getFilteredAssignments() {
        if (filterUserName == null && filterRealmName == null) {
            return assignments;
        }

        return assignments.stream()
                .filter(a -> {
                    boolean matchUser = filterUserName == null || filterUserName.isEmpty() ||
                            (a.getUsername() != null
                                    && a.getUsername().toLowerCase().contains(filterUserName.toLowerCase()));

                    boolean matchRealm = filterRealmName == null || filterRealmName.isEmpty() ||
                            (a.getRealmName() != null
                                    && a.getRealmName().toLowerCase().contains(filterRealmName.toLowerCase()));

                    return matchUser && matchRealm;
                })
                .collect(Collectors.toList());
    }

    /**
     * Obtenir le nombre total d'assignations
     */
    public int getTotalAssignments() {
        return assignments != null ? assignments.size() : 0;
    }

    /**
     * Obtenir le nombre d'assignations actives
     */
    public long getActiveAssignmentsCount() {
        return assignments.stream()
                .filter(RealmAssignmentDTO::isActive)
                .count();
    }

    /**
     * Obtenir le nombre de super admins
     */
    public long getSuperAdminsCount() {
        return assignments.stream()
                .filter(RealmAssignmentDTO::isSuperAdmin)
                .count();
    }

    // Méthodes utilitaires pour les messages
    private void addSuccessMessage(String message) {
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message));
    }

    private void addErrorMessage(String message) {
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message));
    }

    private void addInfoMessage(String message) {
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Information", message));
    }
}
