package dev.lions.user.manager.resource;

import dev.lions.user.manager.dto.role.RoleDTO;
import dev.lions.user.manager.dto.user.UserDTO;
import dev.lions.user.manager.service.SyncService;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotBlank;
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;
import java.util.Map;

/**
 * REST Resource pour la synchronisation avec Keycloak
 */
@Path("/api/sync")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Sync", description = "Synchronisation avec Keycloak et health checks")
@Slf4j
public class SyncResource {

    @Inject
    SyncService syncService;

    @POST
    @Path("/users/{realmName}")
    @Operation(summary = "Synchroniser les utilisateurs", description = "Synchronise tous les utilisateurs depuis Keycloak")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Utilisateurs synchronisés"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "sync_manager"})
    public Response syncUsers(
        @Parameter(description = "Nom du realm") @PathParam("realmName") @NotBlank String realmName
    ) {
        log.info("POST /api/sync/users/{} - Synchronisation des utilisateurs", realmName);

        try {
            int count = syncService.syncUsersFromRealm(realmName);
            return Response.ok(new SyncUsersResponse(count, null)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la synchronisation des utilisateurs du realm {}", realmName, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/roles/realm/{realmName}")
    @Operation(summary = "Synchroniser les rôles realm", description = "Synchronise tous les rôles realm depuis Keycloak")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Rôles realm synchronisés"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "sync_manager"})
    public Response syncRealmRoles(
        @PathParam("realmName") @NotBlank String realmName
    ) {
        log.info("POST /api/sync/roles/realm/{} - Synchronisation des rôles realm", realmName);

        try {
            int count = syncService.syncRolesFromRealm(realmName);
            return Response.ok(new SyncRolesResponse(count, null)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la synchronisation des rôles realm du realm {}", realmName, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/roles/client/{clientId}/{realmName}")
    @Operation(summary = "Synchroniser les rôles client", description = "Synchronise tous les rôles d'un client depuis Keycloak")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Rôles client synchronisés"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "sync_manager"})
    public Response syncClientRoles(
        @PathParam("clientId") @NotBlank String clientId,
        @PathParam("realmName") @NotBlank String realmName
    ) {
        log.info("POST /api/sync/roles/client/{}/{} - Synchronisation des rôles client",
                 clientId, realmName);

        try {
            // Note: syncRolesFromRealm synchronise tous les rôles realm, pas les rôles client spécifiques
            // Pour les rôles client, on synchronise tous les rôles du realm (incluant les rôles client)
            int count = syncService.syncRolesFromRealm(realmName);
            return Response.ok(new SyncRolesResponse(count, null)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la synchronisation des rôles client du client {} (realm: {})",
                     clientId, realmName, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/all/{realmName}")
    @Operation(summary = "Synchronisation complète", description = "Synchronise utilisateurs et rôles depuis Keycloak")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Synchronisation complète effectuée"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "sync_manager"})
    public Response syncAll(
        @PathParam("realmName") @NotBlank String realmName
    ) {
        log.info("POST /api/sync/all/{} - Synchronisation complète", realmName);

        try {
            Map<String, Object> result = syncService.forceSyncRealm(realmName);
            return Response.ok(result).build();
        } catch (Exception e) {
            log.error("Erreur lors de la synchronisation complète du realm {}", realmName, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/health")
    @Operation(summary = "Vérifier la santé de Keycloak", description = "Retourne le statut de santé de Keycloak")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Statut de santé"),
        @APIResponse(responseCode = "503", description = "Keycloak non accessible")
    })
    @RolesAllowed({"admin", "sync_manager", "auditor"})
    public Response checkHealth() {
        log.info("GET /api/sync/health - Vérification de la santé de Keycloak");

        try {
            boolean healthy = syncService.isKeycloakAvailable();
            if (healthy) {
                return Response.ok(new HealthCheckResponse(true, "Keycloak est accessible")).build();
            } else {
                return Response.status(Response.Status.SERVICE_UNAVAILABLE)
                    .entity(new HealthCheckResponse(false, "Keycloak n'est pas accessible"))
                    .build();
            }
        } catch (Exception e) {
            log.error("Erreur lors de la vérification de santé de Keycloak", e);
            return Response.status(Response.Status.SERVICE_UNAVAILABLE)
                .entity(new HealthCheckResponse(false, e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/health/detailed")
    @Operation(summary = "Statut de santé détaillé", description = "Retourne le statut de santé détaillé de Keycloak")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Statut détaillé"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "sync_manager"})
    public Response getDetailedHealthStatus() {
        log.info("GET /api/sync/health/detailed - Statut de santé détaillé");

        try {
            Map<String, Object> status = syncService.getKeycloakHealthInfo();
            return Response.ok(status).build(); // status est maintenant une Map<String, Object>
        } catch (Exception e) {
            log.error("Erreur lors de la récupération du statut de santé détaillé", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/check/realm/{realmName}")
    @Operation(summary = "Vérifier l'existence d'un realm", description = "Vérifie si un realm existe")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Résultat de la vérification"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "sync_manager"})
    public Response checkRealmExists(
        @PathParam("realmName") @NotBlank String realmName
    ) {
        log.info("GET /api/sync/check/realm/{} - Vérification de l'existence", realmName);

        try {
            // Vérifier l'existence du realm en essayant de synchroniser (si ça marche, le realm existe)
            boolean exists = false;
            try {
                syncService.syncUsersFromRealm(realmName);
                exists = true;
            } catch (Exception e) {
                exists = false;
            }
            return Response.ok(new ExistsCheckResponse(exists, "realm", realmName)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la vérification du realm {}", realmName, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/check/user/{userId}")
    @Operation(summary = "Vérifier l'existence d'un utilisateur", description = "Vérifie si un utilisateur existe")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Résultat de la vérification"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "sync_manager"})
    public Response checkUserExists(
        @PathParam("userId") @NotBlank String userId,
        @QueryParam("realm") @NotBlank String realmName
    ) {
        log.info("GET /api/sync/check/user/{} - realm: {}", userId, realmName);

        try {
            // Vérifier l'existence de l'utilisateur n'est plus disponible directement
            // On retourne false car cette fonctionnalité n'est plus dans l'interface
            boolean exists = false;
            return Response.ok(new ExistsCheckResponse(exists, "user", userId)).build();
        } catch (Exception e) {
            log.error("Erreur lors de la vérification de l'utilisateur {} dans le realm {}",
                     userId, realmName, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

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

    @Schema(description = "Réponse de synchronisation d'utilisateurs")
    public static class SyncUsersResponse {
        @Schema(description = "Nombre d'utilisateurs synchronisés")
        public int count;

        @Schema(description = "Liste des utilisateurs synchronisés")
        public List<UserDTO> users;

        public SyncUsersResponse(int count, List<UserDTO> users) {
            this.count = count;
            this.users = users;
        }
    }

    @Schema(description = "Réponse de synchronisation de rôles")
    public static class SyncRolesResponse {
        @Schema(description = "Nombre de rôles synchronisés")
        public int count;

        @Schema(description = "Liste des rôles synchronisés")
        public List<RoleDTO> roles;

        public SyncRolesResponse(int count, List<RoleDTO> roles) {
            this.count = count;
            this.roles = roles;
        }
    }

    @Schema(description = "Réponse de vérification de santé")
    public static class HealthCheckResponse {
        @Schema(description = "Indique si Keycloak est accessible")
        public boolean healthy;

        @Schema(description = "Message descriptif")
        public String message;

        public HealthCheckResponse(boolean healthy, String message) {
            this.healthy = healthy;
            this.message = message;
        }
    }

    @Schema(description = "Réponse de vérification d'existence")
    public static class ExistsCheckResponse {
        @Schema(description = "Indique si la ressource existe")
        public boolean exists;

        @Schema(description = "Type de ressource (realm, user, client, etc.)")
        public String resourceType;

        @Schema(description = "Identifiant de la ressource")
        public String resourceId;

        public ExistsCheckResponse(boolean exists, String resourceType, String resourceId) {
            this.exists = exists;
            this.resourceType = resourceType;
            this.resourceId = resourceId;
        }
    }

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