package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.service.ApprovalService;
import dev.lions.unionflow.server.common.ErrorResponse;
import dev.lions.unionflow.server.api.dto.finance_workflow.request.ApproveTransactionRequest;
import dev.lions.unionflow.server.api.dto.finance_workflow.request.RejectTransactionRequest;
import dev.lions.unionflow.server.api.dto.finance_workflow.response.TransactionApprovalResponse;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Resource REST pour la gestion des approbations de transactions
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-13
 */
@Path("/api/finance/approvals")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Finance - Approvals", description = "Gestion des approbations de transactions financières")
public class ApprovalResource {

    private static final Logger LOG = Logger.getLogger(ApprovalResource.class);

    @Inject
    ApprovalService approvalService;

    @POST
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN", "MEMBRE"})
    @Operation(summary = "Demande une approbation de transaction",
               description = "Crée une demande d'approbation pour une transaction financière")
    public Response requestApproval(Map<String, Object> request) {
        LOG.infof("POST /api/finance/approvals - Request approval");

        try {
            String transactionId = (String) request.get("transactionId");
            String transactionType = (String) request.get("transactionType");
            Number rawAmount = (Number) request.get("amount");
            Double amount = rawAmount != null ? rawAmount.doubleValue() : null;
            String organizationIdStr = (String) request.get("organizationId");

            if (transactionId == null || transactionType == null || amount == null) {
                return Response.status(Response.Status.BAD_REQUEST)
                    .entity(ErrorResponse.of("transactionId, transactionType et amount sont requis"))
                    .build();
            }

            UUID organizationId = organizationIdStr != null ? UUID.fromString(organizationIdStr) : null;

            TransactionApprovalResponse approval = approvalService.requestApproval(
                UUID.fromString(transactionId), transactionType, amount, organizationId);
            return Response.status(Response.Status.CREATED).entity(approval).build();
        } catch (IllegalArgumentException e) {
            return Response.status(Response.Status.BAD_REQUEST)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        } catch (Exception e) {
            LOG.error("Erreur lors de la création de la demande d'approbation", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/pending")
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
    @Operation(summary = "Récupère les approbations en attente",
               description = "Liste toutes les approbations de transactions en attente pour une organisation")
    public Response getPendingApprovals(@QueryParam("organizationId") UUID organizationId) {
        LOG.infof("GET /api/finance/approvals/pending?organizationId=%s", organizationId);

        try {
            List<TransactionApprovalResponse> approvals = approvalService.getPendingApprovals(organizationId);
            return Response.ok(approvals).build();
        } catch (Exception e) {
            LOG.error("Erreur lors de la récupération des approbations en attente", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/{approvalId}")
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
    @Operation(summary = "Récupère une approbation par ID",
               description = "Retourne les détails d'une approbation spécifique")
    public Response getApprovalById(@PathParam("approvalId") UUID approvalId) {
        LOG.infof("GET /api/finance/approvals/%s", approvalId);

        try {
            TransactionApprovalResponse approval = approvalService.getApprovalById(approvalId);
            return Response.ok(approval).build();
        } catch (NotFoundException e) {
            return Response.status(Response.Status.NOT_FOUND)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        } catch (Exception e) {
            LOG.error("Erreur lors de la récupération de l'approbation", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/{approvalId}/approve")
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
    @Operation(summary = "Approuve une transaction",
               description = "Approuve une demande de transaction avec un commentaire optionnel")
    public Response approveTransaction(
            @PathParam("approvalId") UUID approvalId,
            @Valid ApproveTransactionRequest request) {
        LOG.infof("POST /api/finance/approvals/%s/approve", approvalId);

        try {
            TransactionApprovalResponse approval = approvalService.approveTransaction(approvalId, request);
            return Response.ok(approval).build();
        } catch (NotFoundException e) {
            return Response.status(Response.Status.NOT_FOUND)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        } catch (ForbiddenException e) {
            return Response.status(Response.Status.FORBIDDEN)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        } catch (Exception e) {
            LOG.error("Erreur lors de l'approbation de la transaction", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        }
    }

    @POST
    @Path("/{approvalId}/reject")
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
    @Operation(summary = "Rejette une transaction",
               description = "Rejette une demande de transaction avec une raison obligatoire")
    public Response rejectTransaction(
            @PathParam("approvalId") UUID approvalId,
            @Valid RejectTransactionRequest request) {
        LOG.infof("POST /api/finance/approvals/%s/reject", approvalId);

        try {
            TransactionApprovalResponse approval = approvalService.rejectTransaction(approvalId, request);
            return Response.ok(approval).build();
        } catch (NotFoundException e) {
            return Response.status(Response.Status.NOT_FOUND)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        } catch (ForbiddenException e) {
            return Response.status(Response.Status.FORBIDDEN)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        } catch (Exception e) {
            LOG.error("Erreur lors du rejet de la transaction", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/history")
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
    @Operation(summary = "Récupère l'historique des approbations",
               description = "Liste l'historique des approbations avec filtres optionnels")
    public Response getApprovalsHistory(
            @QueryParam("organizationId") UUID organizationId,
            @QueryParam("startDate") String startDateStr,
            @QueryParam("endDate") String endDateStr,
            @QueryParam("status") String status) {
        LOG.infof("GET /api/finance/approvals/history?organizationId=%s&status=%s",
            organizationId, status);

        try {
            LocalDateTime startDate = startDateStr != null ? LocalDateTime.parse(startDateStr) : null;
            LocalDateTime endDate = endDateStr != null ? LocalDateTime.parse(endDateStr) : null;

            List<TransactionApprovalResponse> approvals = approvalService.getApprovalsHistory(
                organizationId, startDate, endDate, status);

            return Response.ok(approvals).build();
        } catch (IllegalArgumentException e) {
            return Response.status(Response.Status.BAD_REQUEST)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        } catch (Exception e) {
            LOG.error("Erreur lors de la récupération de l'historique", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/count/pending")
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
    @Operation(summary = "Compte les approbations en attente",
               description = "Retourne le nombre d'approbations en attente pour une organisation")
    public Response countPendingApprovals(@QueryParam("organizationId") UUID organizationId) {
        LOG.infof("GET /api/finance/approvals/count/pending?organizationId=%s", organizationId);

        try {
            long count = approvalService.countPendingApprovals(organizationId);
            return Response.ok(new CountResponse(count)).build();
        } catch (Exception e) {
            LOG.error("Erreur lors du comptage des approbations", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ErrorResponse.of(e.getMessage()))
                .build();
        }
    }

    // Classe interne pour le comptage
    record CountResponse(long count) {}
}
