package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.config.request.ParametresLcbFtRequest;
import dev.lions.unionflow.server.api.dto.config.response.ParametresLcbFtResponse;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.ParametresLcbFt;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.repository.ParametresLcbFtRepository;
import io.quarkus.cache.CacheResult;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.NotFoundException;
import org.jboss.logging.Logger;

import java.math.BigDecimal;
import java.util.Optional;
import java.util.UUID;

/**
 * Service métier pour la gestion des paramètres LCB-FT (seuils anti-blanchiment).
 *
 * @author lions dev Team
 * @version 1.0
 * @since 2026-03-13
 */
@ApplicationScoped
public class ParametresLcbFtService {

    private static final Logger LOG = Logger.getLogger(ParametresLcbFtService.class);
    private static final String CODE_DEVISE_DEFAULT = "XOF";

    @Inject
    ParametresLcbFtRepository parametresRepository;

    @Inject
    OrganisationRepository organisationRepository;

    /**
     * Récupère les paramètres LCB-FT pour une organisation et une devise.
     * Avec cache pour performance (les seuils changent rarement).
     *
     * @param organisationId ID de l'organisation (null pour paramètres plateforme)
     * @param codeDevise Code devise ISO 4217 (XOF par défaut)
     * @return Paramètres LCB-FT ou paramètres plateforme par défaut
     */
    @CacheResult(cacheName = "parametres-lcb-ft")
    public ParametresLcbFtResponse getParametres(UUID organisationId, String codeDevise) {
        if (codeDevise == null || codeDevise.isBlank()) {
            codeDevise = CODE_DEVISE_DEFAULT;
        }

        LOG.infof("Récupération paramètres LCB-FT pour organisation=%s, devise=%s",
                  organisationId, codeDevise);

        Optional<ParametresLcbFt> params = parametresRepository
            .findByOrganisationAndDevise(organisationId, codeDevise);

        if (params.isEmpty()) {
            LOG.warnf("Aucun paramètre LCB-FT trouvé pour organisation=%s, devise=%s",
                      organisationId, codeDevise);
            throw new NotFoundException(
                "Paramètres LCB-FT non configurés pour cette organisation/devise");
        }

        return toDTO(params.get());
    }

    /**
     * Récupère uniquement le seuil de justification (pour validation rapide).
     *
     * @param organisationId ID de l'organisation
     * @param codeDevise Code devise (XOF par défaut)
     * @return Montant seuil au-dessus duquel origine des fonds est obligatoire
     */
    @CacheResult(cacheName = "seuil-justification-lcb-ft")
    public BigDecimal getSeuilJustification(UUID organisationId, String codeDevise) {
        if (codeDevise == null || codeDevise.isBlank()) {
            codeDevise = CODE_DEVISE_DEFAULT;
        }

        LOG.debugf("Récupération seuil justification LCB-FT org=%s, devise=%s",
                   organisationId, codeDevise);

        return parametresRepository.getSeuilJustification(organisationId, codeDevise)
            .orElse(BigDecimal.valueOf(500000)); // Fallback 500k XOF
    }

    /**
     * Crée ou met à jour les paramètres LCB-FT pour une organisation.
     */
    @Transactional
    public ParametresLcbFtResponse saveOrUpdateParametres(ParametresLcbFtRequest request) {
        LOG.infof("Sauvegarde paramètres LCB-FT pour organisation=%s",
                  request.getOrganisationId());

        Organisation organisation = null;
        if (request.getOrganisationId() != null) {
            organisation = organisationRepository.findByIdOptional(
                UUID.fromString(request.getOrganisationId()))
                .orElseThrow(() -> new NotFoundException(
                    "Organisation non trouvée: " + request.getOrganisationId()));
        }

        // Chercher paramètre existant
        String codeDevise = request.getCodeDevise() != null ?
            request.getCodeDevise() : CODE_DEVISE_DEFAULT;

        UUID orgId = organisation != null ? organisation.getId() : null;
        Optional<ParametresLcbFt> existing = parametresRepository
            .findByOrganisationAndDevise(orgId, codeDevise);

        ParametresLcbFt params;
        if (existing.isPresent()) {
            // Mise à jour
            params = existing.get();
            params.setMontantSeuilJustification(request.getMontantSeuilJustification());
            params.setMontantSeuilValidationManuelle(request.getMontantSeuilValidationManuelle());
            LOG.infof("Paramètres LCB-FT mis à jour : %s", params.getId());
        } else {
            // Création
            params = ParametresLcbFt.builder()
                .organisation(organisation)
                .codeDevise(codeDevise)
                .montantSeuilJustification(request.getMontantSeuilJustification())
                .montantSeuilValidationManuelle(request.getMontantSeuilValidationManuelle())
                .build();
            parametresRepository.persist(params);
            LOG.infof("Nouveaux paramètres LCB-FT créés : %s", params.getId());
        }

        return toDTO(params);
    }

    /**
     * Convertit l'entité en DTO de réponse.
     */
    private ParametresLcbFtResponse toDTO(ParametresLcbFt params) {
        ParametresLcbFtResponse response = ParametresLcbFtResponse.builder()
            .organisationId(params.getOrganisation() != null ?
                params.getOrganisation().getId().toString() : null)
            .organisationNom(params.getOrganisation() != null ?
                params.getOrganisation().getNom() : null)
            .montantSeuilJustification(params.getMontantSeuilJustification())
            .montantSeuilValidationManuelle(params.getMontantSeuilValidationManuelle())
            .codeDevise(params.getCodeDevise())
            .estParametrePlateforme(params.getOrganisation() == null)
            .build();

        // Set BaseResponse fields
        response.setId(params.getId());
        response.setDateCreation(params.getDateCreation());
        response.setDateModification(params.getDateModification());
        response.setActif(params.getActif());

        return response;
    }
}
