package dev.lions.user.manager.resource;

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.service.UserService;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

import java.util.List;

/**
 * REST Resource pour la gestion des utilisateurs
 * Endpoints exposés pour les opérations CRUD sur les utilisateurs Keycloak
 */
@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Users", description = "Gestion des utilisateurs Keycloak")
@Slf4j
public class UserResource {

    @Inject
    UserService userService;

    @POST
    @Path("/search")
    @Operation(summary = "Rechercher des utilisateurs", description = "Recherche d'utilisateurs selon des critères")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Résultats de recherche",
            content = @Content(schema = @Schema(implementation = UserSearchResultDTO.class))),
        @APIResponse(responseCode = "400", description = "Critères invalides"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response searchUsers(@Valid @NotNull UserSearchCriteriaDTO criteria) {
        log.info("POST /api/users/search - Recherche d'utilisateurs");

        try {
            UserSearchResultDTO result = userService.searchUsers(criteria);
            return Response.ok(result).build();
        } catch (Exception e) {
            log.error("Erreur lors de la recherche d'utilisateurs", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/{userId}")
    @Operation(summary = "Récupérer un utilisateur par ID", description = "Récupère les détails d'un utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Utilisateur trouvé",
            content = @Content(schema = @Schema(implementation = UserDTO.class))),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager", "user_viewer"})
    public Response getUserById(
        @Parameter(description = "ID de l'utilisateur") @PathParam("userId") @NotBlank String userId,
        @Parameter(description = "Nom du realm") @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("GET /api/users/{} - realm: {}", userId, realmName);

        try {
            return userService.getUserById(userId, realmName)
                .map(user -> Response.ok(user).build())
                .orElse(Response.status(Response.Status.NOT_FOUND)
                    .entity(new ErrorResponse("Utilisateur non trouvé"))
                    .build());
        } catch (Exception e) {
            log.error("Erreur lors de la récupération de l'utilisateur {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Operation(summary = "Lister tous les utilisateurs", description = "Liste paginée de tous les utilisateurs")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des utilisateurs",
            content = @Content(schema = @Schema(implementation = UserSearchResultDTO.class))),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager", "user_viewer"})
    public Response getAllUsers(
        @QueryParam("realm") @NotBlank String realmName,
        @QueryParam("page") @DefaultValue("0") int page,
        @QueryParam("pageSize") @DefaultValue("20") int pageSize
    ) {
        log.info("GET /api/users - realm: {}, page: {}, pageSize: {}", realmName, page, pageSize);

        try {
            UserSearchResultDTO result = userService.getAllUsers(realmName, page, pageSize);
            return Response.ok(result).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des utilisateurs", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Operation(summary = "Créer un utilisateur", description = "Crée un nouvel utilisateur dans Keycloak")
    @APIResponses({
        @APIResponse(responseCode = "201", description = "Utilisateur créé",
            content = @Content(schema = @Schema(implementation = UserDTO.class))),
        @APIResponse(responseCode = "400", description = "Données invalides"),
        @APIResponse(responseCode = "409", description = "Utilisateur existe déjà"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response createUser(
        @Valid @NotNull UserDTO user,
        @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("POST /api/users - Création d'un utilisateur: {}", user.getUsername());

        try {
            UserDTO createdUser = userService.createUser(user, realmName);
            return Response.status(Response.Status.CREATED).entity(createdUser).build();
        } catch (IllegalArgumentException e) {
            log.warn("Données invalides lors de la création: {}", e.getMessage());
            return Response.status(Response.Status.CONFLICT)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        } catch (Exception e) {
            log.error("Erreur lors de la création de l'utilisateur", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @PUT
    @Path("/{userId}")
    @Operation(summary = "Mettre à jour un utilisateur", description = "Met à jour les informations d'un utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Utilisateur mis à jour",
            content = @Content(schema = @Schema(implementation = UserDTO.class))),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "400", description = "Données invalides"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response updateUser(
        @PathParam("userId") @NotBlank String userId,
        @Valid @NotNull UserDTO user,
        @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("PUT /api/users/{} - Mise à jour", userId);

        try {
            UserDTO updatedUser = userService.updateUser(userId, user, realmName);
            return Response.ok(updatedUser).build();
        } catch (RuntimeException e) {
            if (e.getMessage().contains("non trouvé")) {
                return Response.status(Response.Status.NOT_FOUND)
                    .entity(new ErrorResponse(e.getMessage()))
                    .build();
            }
            log.error("Erreur lors de la mise à jour de l'utilisateur {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @DELETE
    @Path("/{userId}")
    @Operation(summary = "Supprimer un utilisateur", description = "Supprime un utilisateur (soft ou hard delete)")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Utilisateur supprimé"),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response deleteUser(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName,
        @QueryParam("hardDelete") @DefaultValue("false") boolean hardDelete
    ) {
        log.info("DELETE /api/users/{} - realm: {}, hardDelete: {}", userId, realmName, hardDelete);

        try {
            userService.deleteUser(userId, realmName, hardDelete);
            return Response.noContent().build();
        } catch (RuntimeException e) {
            if (e.getMessage().contains("non trouvé")) {
                return Response.status(Response.Status.NOT_FOUND)
                    .entity(new ErrorResponse(e.getMessage()))
                    .build();
            }
            log.error("Erreur lors de la suppression de l'utilisateur {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/{userId}/activate")
    @Operation(summary = "Activer un utilisateur", description = "Active un utilisateur désactivé")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Utilisateur activé"),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response activateUser(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("POST /api/users/{}/activate", userId);

        try {
            userService.activateUser(userId, realmName);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de l'activation de l'utilisateur {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/{userId}/deactivate")
    @Operation(summary = "Désactiver un utilisateur", description = "Désactive un utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Utilisateur désactivé"),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response deactivateUser(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName,
        @QueryParam("raison") String raison
    ) {
        log.info("POST /api/users/{}/deactivate - raison: {}", userId, raison);

        try {
            userService.deactivateUser(userId, realmName, raison);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de la désactivation de l'utilisateur {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/{userId}/reset-password")
    @Operation(summary = "Réinitialiser le mot de passe", description = "Définit un nouveau mot de passe pour l'utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Mot de passe réinitialisé"),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response resetPassword(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName,
        @NotNull PasswordResetRequest request
    ) {
        log.info("POST /api/users/{}/reset-password - temporary: {}", userId, request.temporary);

        try {
            userService.resetPassword(userId, realmName, request.password, request.temporary);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de la réinitialisation du mot de passe pour {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/{userId}/send-verification-email")
    @Operation(summary = "Envoyer email de vérification", description = "Envoie un email de vérification à l'utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Email envoyé"),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response sendVerificationEmail(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("POST /api/users/{}/send-verification-email", userId);

        try {
            userService.sendVerificationEmail(userId, realmName);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de l'envoi de l'email de vérification pour {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/{userId}/logout-sessions")
    @Operation(summary = "Déconnecter toutes les sessions", description = "Révoque toutes les sessions actives de l'utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Sessions révoquées"),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response logoutAllSessions(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("POST /api/users/{}/logout-sessions", userId);

        try {
            int count = userService.logoutAllSessions(userId, realmName);
            return Response.ok(new SessionsRevokedResponse(count)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la déconnexion des sessions pour {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/{userId}/sessions")
    @Operation(summary = "Récupérer les sessions actives", description = "Liste les sessions actives de l'utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des sessions"),
        @APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager", "user_viewer"})
    public Response getActiveSessions(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("GET /api/users/{}/sessions", userId);

        try {
            List<String> sessions = userService.getActiveSessions(userId, realmName);
            return Response.ok(sessions).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des sessions pour {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    // ==================== DTOs internes ====================

    @Schema(description = "Requête de réinitialisation de mot de passe")
    public static class PasswordResetRequest {
        @Schema(description = "Nouveau mot de passe", required = true)
        public String password;

        @Schema(description = "Indique si le mot de passe est temporaire", defaultValue = "true")
        public boolean temporary = true;
    }

    @Schema(description = "Réponse de révocation de sessions")
    public static class SessionsRevokedResponse {
        @Schema(description = "Nombre de sessions révoquées")
        public int count;

        public SessionsRevokedResponse(int count) {
            this.count = count;
        }
    }

    @Schema(description = "Réponse d'erreur")
    public static class ErrorResponse {
        @Schema(description = "Message d'erreur")
        public String message;

        public ErrorResponse(String message) {
            this.message = message;
        }
    }
}
