package dev.lions.user.manager.resource;

import dev.lions.user.manager.dto.realm.RealmAssignmentDTO;
import dev.lions.user.manager.service.RealmAuthorizationService;
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.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;
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 affectations de realms aux utilisateurs
 * Permet le contrôle d'accès multi-tenant
 */
@Path("/api/realm-assignments")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Realm Assignments", description = "Gestion des affectations de realms (contrôle d'accès multi-tenant)")
@Slf4j
public class RealmAssignmentResource {

    @Inject
    RealmAuthorizationService realmAuthorizationService;

    @Context
    SecurityContext securityContext;

    // ==================== Endpoints de consultation ====================

    @GET
    @Operation(summary = "Lister toutes les affectations", description = "Liste toutes les affectations de realms")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des affectations"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response getAllAssignments() {
        log.info("GET /api/realm-assignments - Récupération de toutes les affectations");

        try {
            List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAllAssignments();
            return Response.ok(assignments).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des affectations", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/user/{userId}")
    @Operation(summary = "Affectations par utilisateur", description = "Liste les realms assignés à un utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des affectations"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response getAssignmentsByUser(
        @Parameter(description = "ID de l'utilisateur") @PathParam("userId") @NotBlank String userId
    ) {
        log.info("GET /api/realm-assignments/user/{}", userId);

        try {
            List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAssignmentsByUser(userId);
            return Response.ok(assignments).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des affectations pour l'utilisateur {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/realm/{realmName}")
    @Operation(summary = "Affectations par realm", description = "Liste les utilisateurs ayant accès à un realm")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des affectations"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response getAssignmentsByRealm(
        @Parameter(description = "Nom du realm") @PathParam("realmName") @NotBlank String realmName
    ) {
        log.info("GET /api/realm-assignments/realm/{}", realmName);

        try {
            List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAssignmentsByRealm(realmName);
            return Response.ok(assignments).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des affectations pour le realm {}", realmName, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/{assignmentId}")
    @Operation(summary = "Récupérer une affectation", description = "Récupère une affectation par son ID")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Affectation trouvée"),
        @APIResponse(responseCode = "404", description = "Affectation non trouvée"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response getAssignmentById(
        @Parameter(description = "ID de l'affectation") @PathParam("assignmentId") @NotBlank String assignmentId
    ) {
        log.info("GET /api/realm-assignments/{}", assignmentId);

        try {
            return realmAuthorizationService.getAssignmentById(assignmentId)
                .map(assignment -> Response.ok(assignment).build())
                .orElse(Response.status(Response.Status.NOT_FOUND)
                    .entity(new ErrorResponse("Affectation non trouvée"))
                    .build());
        } catch (Exception e) {
            log.error("Erreur lors de la récupération de l'affectation {}", assignmentId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    // ==================== Endpoints de vérification ====================

    @GET
    @Path("/check")
    @Operation(summary = "Vérifier l'accès", description = "Vérifie si un utilisateur peut gérer un realm")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Vérification effectuée"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response canManageRealm(
        @Parameter(description = "ID de l'utilisateur") @QueryParam("userId") @NotBlank String userId,
        @Parameter(description = "Nom du realm") @QueryParam("realmName") @NotBlank String realmName
    ) {
        log.info("GET /api/realm-assignments/check - userId: {}, realmName: {}", userId, realmName);

        try {
            boolean canManage = realmAuthorizationService.canManageRealm(userId, realmName);
            return Response.ok(new CheckResponse(canManage, userId, realmName)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la vérification d'accès", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/authorized-realms/{userId}")
    @Operation(summary = "Realms autorisés", description = "Liste les realms qu'un utilisateur peut gérer")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des realms"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "user_manager"})
    public Response getAuthorizedRealms(
        @Parameter(description = "ID de l'utilisateur") @PathParam("userId") @NotBlank String userId
    ) {
        log.info("GET /api/realm-assignments/authorized-realms/{}", userId);

        try {
            List<String> realms = realmAuthorizationService.getAuthorizedRealms(userId);
            boolean isSuperAdmin = realmAuthorizationService.isSuperAdmin(userId);
            return Response.ok(new AuthorizedRealmsResponse(realms, isSuperAdmin)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des realms autorisés pour {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    // ==================== Endpoints de modification ====================

    @POST
    @Operation(summary = "Assigner un realm", description = "Assigne un realm à un utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "201", description = "Affectation créée",
            content = @Content(schema = @Schema(implementation = RealmAssignmentDTO.class))),
        @APIResponse(responseCode = "400", description = "Données invalides"),
        @APIResponse(responseCode = "409", description = "Affectation existe déjà"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response assignRealmToUser(@Valid @NotNull RealmAssignmentDTO assignment) {
        log.info("POST /api/realm-assignments - Assignation du realm {} à l'utilisateur {}",
            assignment.getRealmName(), assignment.getUserId());

        try {
            // Ajouter l'utilisateur qui fait l'assignation
            if (securityContext.getUserPrincipal() != null) {
                assignment.setAssignedBy(securityContext.getUserPrincipal().getName());
            }

            RealmAssignmentDTO createdAssignment = realmAuthorizationService.assignRealmToUser(assignment);
            return Response.status(Response.Status.CREATED).entity(createdAssignment).build();
        } catch (IllegalArgumentException e) {
            log.warn("Données invalides lors de l'assignation: {}", e.getMessage());
            return Response.status(Response.Status.CONFLICT)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        } catch (Exception e) {
            log.error("Erreur lors de l'assignation du realm", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @DELETE
    @Path("/user/{userId}/realm/{realmName}")
    @Operation(summary = "Révoquer un realm", description = "Retire l'accès d'un utilisateur à un realm")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Affectation révoquée"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response revokeRealmFromUser(
        @Parameter(description = "ID de l'utilisateur") @PathParam("userId") @NotBlank String userId,
        @Parameter(description = "Nom du realm") @PathParam("realmName") @NotBlank String realmName
    ) {
        log.info("DELETE /api/realm-assignments/user/{}/realm/{}", userId, realmName);

        try {
            realmAuthorizationService.revokeRealmFromUser(userId, realmName);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de la révocation du realm {} pour {}", realmName, userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @DELETE
    @Path("/user/{userId}")
    @Operation(summary = "Révoquer tous les realms", description = "Retire tous les accès d'un utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Affectations révoquées"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response revokeAllRealmsFromUser(
        @Parameter(description = "ID de l'utilisateur") @PathParam("userId") @NotBlank String userId
    ) {
        log.info("DELETE /api/realm-assignments/user/{}", userId);

        try {
            realmAuthorizationService.revokeAllRealmsFromUser(userId);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de la révocation de tous les realms pour {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @PUT
    @Path("/{assignmentId}/deactivate")
    @Operation(summary = "Désactiver une affectation", description = "Désactive une affectation sans la supprimer")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Affectation désactivée"),
        @APIResponse(responseCode = "404", description = "Affectation non trouvée"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response deactivateAssignment(
        @Parameter(description = "ID de l'affectation") @PathParam("assignmentId") @NotBlank String assignmentId
    ) {
        log.info("PUT /api/realm-assignments/{}/deactivate", assignmentId);

        try {
            realmAuthorizationService.deactivateAssignment(assignmentId);
            return Response.noContent().build();
        } catch (IllegalArgumentException e) {
            return Response.status(Response.Status.NOT_FOUND)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        } catch (Exception e) {
            log.error("Erreur lors de la désactivation de l'affectation {}", assignmentId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @PUT
    @Path("/{assignmentId}/activate")
    @Operation(summary = "Activer une affectation", description = "Réactive une affectation")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Affectation activée"),
        @APIResponse(responseCode = "404", description = "Affectation non trouvée"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response activateAssignment(
        @Parameter(description = "ID de l'affectation") @PathParam("assignmentId") @NotBlank String assignmentId
    ) {
        log.info("PUT /api/realm-assignments/{}/activate", assignmentId);

        try {
            realmAuthorizationService.activateAssignment(assignmentId);
            return Response.noContent().build();
        } catch (IllegalArgumentException e) {
            return Response.status(Response.Status.NOT_FOUND)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        } catch (Exception e) {
            log.error("Erreur lors de l'activation de l'affectation {}", assignmentId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @PUT
    @Path("/super-admin/{userId}")
    @Operation(summary = "Définir super admin", description = "Définit ou retire le statut de super admin")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Statut modifié"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response setSuperAdmin(
        @Parameter(description = "ID de l'utilisateur") @PathParam("userId") @NotBlank String userId,
        @Parameter(description = "Super admin (true/false)") @QueryParam("superAdmin") @NotNull Boolean superAdmin
    ) {
        log.info("PUT /api/realm-assignments/super-admin/{} - superAdmin: {}", userId, superAdmin);

        try {
            realmAuthorizationService.setSuperAdmin(userId, superAdmin);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de la modification du statut super admin pour {}", userId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    // ==================== Classes internes pour les réponses ====================

    @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;
        }
    }

    @Schema(description = "Réponse de vérification d'accès")
    public static class CheckResponse {
        @Schema(description = "L'utilisateur peut gérer le realm")
        public boolean canManage;

        @Schema(description = "ID de l'utilisateur")
        public String userId;

        @Schema(description = "Nom du realm")
        public String realmName;

        public CheckResponse(boolean canManage, String userId, String realmName) {
            this.canManage = canManage;
            this.userId = userId;
            this.realmName = realmName;
        }
    }

    @Schema(description = "Réponse des realms autorisés")
    public static class AuthorizedRealmsResponse {
        @Schema(description = "Liste des realms (vide si super admin)")
        public List<String> realms;

        @Schema(description = "L'utilisateur est super admin")
        public boolean isSuperAdmin;

        public AuthorizedRealmsResponse(List<String> realms, boolean isSuperAdmin) {
            this.realms = realms;
            this.isSuperAdmin = isSuperAdmin;
        }
    }
}
