package dev.lions.user.manager.service.impl;

import dev.lions.user.manager.client.KeycloakAdminClient;
import dev.lions.user.manager.dto.user.UserDTO;
import dev.lions.user.manager.dto.user.UserSearchCriteriaDTO;
import dev.lions.user.manager.dto.user.UserSearchResultDTO;
import dev.lions.user.manager.mapper.UserMapper;
import dev.lions.user.manager.service.UserService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Implémentation du service de gestion des utilisateurs
 * Utilise UNIQUEMENT l'API Admin Keycloak (ZERO accès direct à la DB Keycloak)
 */
@ApplicationScoped
@Slf4j
public class UserServiceImpl implements UserService {

    @Inject
    KeycloakAdminClient keycloakAdminClient;

    @Override
    public UserSearchResultDTO searchUsers(@Valid @NotNull UserSearchCriteriaDTO criteria) {
        log.info("Recherche d'utilisateurs avec critères: {}", criteria);

        try {
            String realmName = criteria.getRealmName();
            UsersResource usersResource = keycloakAdminClient.getUsers(realmName);

            // Construire la requête de recherche
            List<UserRepresentation> users;

            if (criteria.getSearchTerm() != null && !criteria.getSearchTerm().isBlank()) {
                // Recherche globale
                users = usersResource.search(
                    criteria.getSearchTerm(),
                    criteria.getOffset(),
                    criteria.getPageSize()
                );
            } else if (criteria.getUsername() != null) {
                // Recherche par username exact
                users = usersResource.search(
                    criteria.getUsername(),
                    criteria.getOffset(),
                    criteria.getPageSize(),
                    true // exact match
                );
            } else if (criteria.getEmail() != null) {
                // Recherche par email
                users = usersResource.searchByEmail(
                    criteria.getEmail(),
                    true // exact match
                );
            } else {
                // Liste tous les utilisateurs
                users = usersResource.list(
                    criteria.getOffset(),
                    criteria.getPageSize()
                );
            }

            // Filtrer selon les critères supplémentaires
            users = filterUsers(users, criteria);

            // Convertir en DTOs
            List<UserDTO> userDTOs = UserMapper.toDTOList(users, realmName);

            // Compter le total
            long totalCount = usersResource.count();

            // Construire le résultat paginé
            return UserSearchResultDTO.of(userDTOs, criteria, totalCount);

        } catch (Exception e) {
            log.error("Erreur lors de la recherche d'utilisateurs", e);
            throw new RuntimeException("Impossible de rechercher les utilisateurs", e);
        }
    }

    @Override
    public Optional<UserDTO> getUserById(@NotBlank String userId, @NotBlank String realmName) {
        log.info("Récupération de l'utilisateur {} dans le realm {}", userId, realmName);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);
            UserRepresentation userRep = userResource.toRepresentation();
            return Optional.of(UserMapper.toDTO(userRep, realmName));
        } catch (NotFoundException e) {
            log.warn("Utilisateur {} non trouvé dans le realm {}", userId, realmName);
            return Optional.empty();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération de l'utilisateur {}", userId, e);
            throw new RuntimeException("Impossible de récupérer l'utilisateur", e);
        }
    }

    @Override
    public Optional<UserDTO> getUserByUsername(@NotBlank String username, @NotBlank String realmName) {
        log.info("Récupération de l'utilisateur par username {} dans le realm {}", username, realmName);

        try {
            List<UserRepresentation> users = keycloakAdminClient.getUsers(realmName)
                .search(username, 0, 1, true);

            if (users.isEmpty()) {
                return Optional.empty();
            }

            return Optional.of(UserMapper.toDTO(users.get(0), realmName));
        } catch (Exception e) {
            log.error("Erreur lors de la récupération de l'utilisateur par username {}", username, e);
            throw new RuntimeException("Impossible de récupérer l'utilisateur", e);
        }
    }

    @Override
    public Optional<UserDTO> getUserByEmail(@NotBlank String email, @NotBlank String realmName) {
        log.info("Récupération de l'utilisateur par email {} dans le realm {}", email, realmName);

        try {
            List<UserRepresentation> users = keycloakAdminClient.getUsers(realmName)
                .searchByEmail(email, true);

            if (users.isEmpty()) {
                return Optional.empty();
            }

            return Optional.of(UserMapper.toDTO(users.get(0), realmName));
        } catch (Exception e) {
            log.error("Erreur lors de la récupération de l'utilisateur par email {}", email, e);
            throw new RuntimeException("Impossible de récupérer l'utilisateur", e);
        }
    }

    @Override
    public UserDTO createUser(@Valid @NotNull UserDTO user, @NotBlank String realmName) {
        log.info("Création de l'utilisateur {} dans le realm {}", user.getUsername(), realmName);

        try {
            // Vérifier si l'utilisateur existe déjà
            if (usernameExists(user.getUsername(), realmName)) {
                throw new IllegalArgumentException("Le nom d'utilisateur existe déjà: " + user.getUsername());
            }

            if (user.getEmail() != null && emailExists(user.getEmail(), realmName)) {
                throw new IllegalArgumentException("L'email existe déjà: " + user.getEmail());
            }

            // Convertir DTO vers UserRepresentation
            UserRepresentation userRep = UserMapper.toRepresentation(user);

            // Créer l'utilisateur
            UsersResource usersResource = keycloakAdminClient.getUsers(realmName);
            var response = usersResource.create(userRep);

            if (response.getStatus() != 201) {
                throw new RuntimeException("Échec de la création de l'utilisateur: " + response.getStatusInfo());
            }

            // Récupérer l'ID de l'utilisateur créé
            String userId = response.getLocation().getPath().replaceAll(".*/([^/]+)$", "$1");

            // Définir le mot de passe si fourni
            if (user.getTemporaryPassword() != null) {
                setPassword(userId, realmName, user.getTemporaryPassword(),
                    user.getTemporaryPasswordFlag() != null && user.getTemporaryPasswordFlag());
            }

            // Récupérer l'utilisateur créé
            UserResource userResource = usersResource.get(userId);
            UserRepresentation createdUser = userResource.toRepresentation();

            log.info("✅ Utilisateur créé avec succès: {} (ID: {})", user.getUsername(), userId);
            return UserMapper.toDTO(createdUser, realmName);

        } catch (Exception e) {
            log.error("❌ Erreur lors de la création de l'utilisateur {}", user.getUsername(), e);
            throw new RuntimeException("Impossible de créer l'utilisateur", e);
        }
    }

    @Override
    public UserDTO updateUser(@NotBlank String userId, @Valid @NotNull UserDTO user, @NotBlank String realmName) {
        log.info("Mise à jour de l'utilisateur {} dans le realm {}", userId, realmName);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);

            // Récupérer l'utilisateur existant
            UserRepresentation existingUser = userResource.toRepresentation();

            // Mettre à jour les champs
            if (user.getEmail() != null) {
                existingUser.setEmail(user.getEmail());
            }
            if (user.getPrenom() != null) {
                existingUser.setFirstName(user.getPrenom());
            }
            if (user.getNom() != null) {
                existingUser.setLastName(user.getNom());
            }
            if (user.getEnabled() != null) {
                existingUser.setEnabled(user.getEnabled());
            }
            if (user.getEmailVerified() != null) {
                existingUser.setEmailVerified(user.getEmailVerified());
            }
            if (user.getAttributes() != null) {
                existingUser.setAttributes(user.getAttributes());
            }

            // Envoyer la mise à jour
            userResource.update(existingUser);

            // Récupérer l'utilisateur mis à jour
            UserRepresentation updatedUser = userResource.toRepresentation();

            log.info("✅ Utilisateur mis à jour avec succès: {}", userId);
            return UserMapper.toDTO(updatedUser, realmName);

        } catch (NotFoundException e) {
            log.error("❌ Utilisateur {} non trouvé", userId);
            throw new RuntimeException("Utilisateur non trouvé", e);
        } catch (Exception e) {
            log.error("❌ Erreur lors de la mise à jour de l'utilisateur {}", userId, e);
            throw new RuntimeException("Impossible de mettre à jour l'utilisateur", e);
        }
    }

    @Override
    public void deleteUser(@NotBlank String userId, @NotBlank String realmName, boolean hardDelete) {
        log.info("Suppression de l'utilisateur {} dans le realm {} (hard: {})", userId, realmName, hardDelete);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);

            if (hardDelete) {
                // Suppression définitive
                userResource.remove();
                log.info("✅ Utilisateur supprimé définitivement: {}", userId);
            } else {
                // Soft delete: désactiver l'utilisateur
                UserRepresentation user = userResource.toRepresentation();
                user.setEnabled(false);
                userResource.update(user);
                log.info("✅ Utilisateur désactivé (soft delete): {}", userId);
            }

        } catch (NotFoundException e) {
            log.error("❌ Utilisateur {} non trouvé", userId);
            throw new RuntimeException("Utilisateur non trouvé", e);
        } catch (Exception e) {
            log.error("❌ Erreur lors de la suppression de l'utilisateur {}", userId, e);
            throw new RuntimeException("Impossible de supprimer l'utilisateur", e);
        }
    }

    @Override
    public void activateUser(@NotBlank String userId, @NotBlank String realmName) {
        log.info("Activation de l'utilisateur {} dans le realm {}", userId, realmName);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);
            UserRepresentation user = userResource.toRepresentation();
            user.setEnabled(true);
            userResource.update(user);

            log.info("✅ Utilisateur activé: {}", userId);
        } catch (Exception e) {
            log.error("❌ Erreur lors de l'activation de l'utilisateur {}", userId, e);
            throw new RuntimeException("Impossible d'activer l'utilisateur", e);
        }
    }

    @Override
    public void deactivateUser(@NotBlank String userId, @NotBlank String realmName, String raison) {
        log.info("Désactivation de l'utilisateur {} dans le realm {} (raison: {})", userId, realmName, raison);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);
            UserRepresentation user = userResource.toRepresentation();
            user.setEnabled(false);
            userResource.update(user);

            log.info("✅ Utilisateur désactivé: {}", userId);
        } catch (Exception e) {
            log.error("❌ Erreur lors de la désactivation de l'utilisateur {}", userId, e);
            throw new RuntimeException("Impossible de désactiver l'utilisateur", e);
        }
    }

    @Override
    public void suspendUser(@NotBlank String userId, @NotBlank String realmName, String raison, int duree) {
        log.info("Suspension de l'utilisateur {} dans le realm {} (raison: {}, durée: {} jours)",
            userId, realmName, raison, duree);

        deactivateUser(userId, realmName, raison);
    }

    @Override
    public void unlockUser(@NotBlank String userId, @NotBlank String realmName) {
        log.info("Déverrouillage de l'utilisateur {} dans le realm {}", userId, realmName);
        activateUser(userId, realmName);
    }

    @Override
    public void resetPassword(@NotBlank String userId, @NotBlank String realmName,
                            @NotBlank String temporaryPassword, boolean temporary) {
        log.info("Réinitialisation du mot de passe pour l'utilisateur {} (temporaire: {})", userId, temporary);

        setPassword(userId, realmName, temporaryPassword, temporary);
    }

    @Override
    public void sendVerificationEmail(@NotBlank String userId, @NotBlank String realmName) {
        log.info("Envoi d'un email de vérification à l'utilisateur {}", userId);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);
            userResource.sendVerifyEmail();

            log.info("✅ Email de vérification envoyé: {}", userId);
        } catch (Exception e) {
            log.error("❌ Erreur lors de l'envoi de l'email de vérification pour {}", userId, e);
            throw new RuntimeException("Impossible d'envoyer l'email de vérification", e);
        }
    }

    @Override
    public int logoutAllSessions(@NotBlank String userId, @NotBlank String realmName) {
        log.info("Déconnexion de toutes les sessions pour l'utilisateur {}", userId);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);
            int sessionsCount = userResource.getUserSessions().size();
            userResource.logout();

            log.info("✅ {} sessions révoquées pour l'utilisateur {}", sessionsCount, userId);
            return sessionsCount;
        } catch (Exception e) {
            log.error("❌ Erreur lors de la déconnexion des sessions pour {}", userId, e);
            throw new RuntimeException("Impossible de déconnecter les sessions", e);
        }
    }

    @Override
    public List<String> getActiveSessions(@NotBlank String userId, @NotBlank String realmName) {
        log.info("Récupération des sessions actives pour l'utilisateur {}", userId);

        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);
            return userResource.getUserSessions().stream()
                .map(session -> session.getId())
                .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("❌ Erreur lors de la récupération des sessions pour {}", userId, e);
            return Collections.emptyList();
        }
    }

    @Override
    public long countUsers(@NotNull UserSearchCriteriaDTO criteria) {
        try {
            return keycloakAdminClient.getUsers(criteria.getRealmName()).count();
        } catch (Exception e) {
            log.error("Erreur lors du comptage des utilisateurs", e);
            return 0;
        }
    }

    @Override
    public UserSearchResultDTO getAllUsers(@NotBlank String realmName, int page, int pageSize) {
        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(realmName)
            .page(page)
            .pageSize(pageSize)
            .build();

        return searchUsers(criteria);
    }

    @Override
    public boolean usernameExists(@NotBlank String username, @NotBlank String realmName) {
        try {
            List<UserRepresentation> users = keycloakAdminClient.getUsers(realmName)
                .search(username, 0, 1, true);
            return !users.isEmpty();
        } catch (Exception e) {
            log.error("Erreur lors de la vérification de l'existence du username {}", username, e);
            return false;
        }
    }

    @Override
    public boolean emailExists(@NotBlank String email, @NotBlank String realmName) {
        try {
            List<UserRepresentation> users = keycloakAdminClient.getUsers(realmName)
                .searchByEmail(email, true);
            return !users.isEmpty();
        } catch (Exception e) {
            log.error("Erreur lors de la vérification de l'existence de l'email {}", email, e);
            return false;
        }
    }

    @Override
    public String exportUsersToCSV(@NotNull UserSearchCriteriaDTO criteria) {
        // TODO: Implémenter l'export CSV
        throw new UnsupportedOperationException("Export CSV non implémenté");
    }

    @Override
    public int importUsersFromCSV(@NotBlank String csvContent, @NotBlank String realmName) {
        // TODO: Implémenter l'import CSV
        throw new UnsupportedOperationException("Import CSV non implémenté");
    }

    // ==================== Méthodes privées ====================

    private void setPassword(String userId, String realmName, String password, boolean temporary) {
        try {
            UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);

            CredentialRepresentation credential = new CredentialRepresentation();
            credential.setType(CredentialRepresentation.PASSWORD);
            credential.setValue(password);
            credential.setTemporary(temporary);

            userResource.resetPassword(credential);

            log.info("✅ Mot de passe défini pour l'utilisateur {} (temporaire: {})", userId, temporary);
        } catch (Exception e) {
            log.error("❌ Erreur lors de la définition du mot de passe pour {}", userId, e);
            throw new RuntimeException("Impossible de définir le mot de passe", e);
        }
    }

    private List<UserRepresentation> filterUsers(List<UserRepresentation> users, UserSearchCriteriaDTO criteria) {
        return users.stream()
            .filter(user -> {
                // Filtrer par enabled
                if (criteria.getEnabled() != null && !criteria.getEnabled().equals(user.isEnabled())) {
                    return false;
                }

                // Filtrer par emailVerified
                if (criteria.getEmailVerified() != null && !criteria.getEmailVerified().equals(user.isEmailVerified())) {
                    return false;
                }

                // TODO: Ajouter d'autres filtres selon les besoins

                return true;
            })
            .collect(Collectors.toList());
    }
}
