package dev.lions.unionflow.server.service.kyc;

import dev.lions.unionflow.server.api.dto.kyc.request.CreateBeneficiaireEffectifRequest;
import dev.lions.unionflow.server.api.dto.kyc.request.UpdateBeneficiaireEffectifRequest;
import dev.lions.unionflow.server.api.dto.kyc.response.BeneficiaireEffectifResponse;
import dev.lions.unionflow.server.entity.BeneficiaireEffectif;
import dev.lions.unionflow.server.entity.KycDossier;
import dev.lions.unionflow.server.repository.BeneficiaireEffectifRepository;
import dev.lions.unionflow.server.repository.KycDossierRepository;
import dev.lions.unionflow.server.security.AuditTrailService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.NotFoundException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.jboss.logging.Logger;

/**
 * Service métier des Bénéficiaires Effectifs (UBO).
 *
 * <p>Couche unique pour business logic UBO. Valide les règles GAFI/BCEAO :
 * <ul>
 *   <li>Somme des pourcentages capital ne dépasse pas 100 %</li>
 *   <li>Au moins un identifiant cible (kycDossierId, organisationCibleId, ou membreId)</li>
 *   <li>Cohérence PEP (si {@code estPep=true}, alerte audit trail)</li>
 * </ul>
 *
 * <p>Les vérifications de rôles sont déléguées à la Resource via {@code @RolesAllowed}.
 *
 * @since 2026-04-25 (Sprint 10)
 */
@ApplicationScoped
public class BeneficiaireEffectifService {

  private static final Logger LOG = Logger.getLogger(BeneficiaireEffectifService.class);
  private static final BigDecimal CENT = new BigDecimal("100.00");

  @Inject BeneficiaireEffectifRepository repository;
  @Inject KycDossierRepository kycDossierRepository;
  @Inject AuditTrailService auditTrail;

  // ────────────────────────────────────────────────────────────
  // Lecture
  // ────────────────────────────────────────────────────────────

  public List<BeneficiaireEffectifResponse> listerParKycDossier(UUID kycDossierId) {
    return repository.findByKycDossier(kycDossierId).stream().map(this::toResponse).toList();
  }

  public List<BeneficiaireEffectifResponse> listerParOrganisationCible(UUID organisationCibleId) {
    return repository.findByOrganisationCible(organisationCibleId).stream().map(this::toResponse)
        .toList();
  }

  public List<BeneficiaireEffectifResponse> listerPep() {
    return repository.findPep().stream().map(this::toResponse).toList();
  }

  public BeneficiaireEffectifResponse trouverParId(UUID id) {
    BeneficiaireEffectif e = repository.findById(id);
    if (e == null) throw new NotFoundException("UBO introuvable : " + id);
    return toResponse(e);
  }

  // ────────────────────────────────────────────────────────────
  // Écriture
  // ────────────────────────────────────────────────────────────

  @Transactional
  public BeneficiaireEffectifResponse creer(CreateBeneficiaireEffectifRequest req) {
    if (req.kycDossierId() == null && req.organisationCibleId() == null && req.membreId() == null) {
      throw new IllegalArgumentException(
          "Au moins un identifiant cible obligatoire : kycDossierId, organisationCibleId ou membreId");
    }

    KycDossier kyc = null;
    if (req.kycDossierId() != null) {
      kyc = kycDossierRepository.findById(req.kycDossierId());
      if (kyc == null) {
        throw new NotFoundException("KycDossier introuvable : " + req.kycDossierId());
      }
      verifierSeuilPourcentage(req.kycDossierId(), null, req.pourcentageCapital());
    }

    BeneficiaireEffectif e = BeneficiaireEffectif.builder()
        .kycDossier(kyc)
        .organisationCibleId(req.organisationCibleId())
        .membreId(req.membreId())
        .nom(req.nom())
        .prenoms(req.prenoms())
        .dateNaissance(req.dateNaissance())
        .lieuNaissance(req.lieuNaissance())
        .nationalite(req.nationalite())
        .paysResidence(req.paysResidence())
        .typePieceIdentite(req.typePieceIdentite())
        .numeroPieceIdentite(req.numeroPieceIdentite())
        .dateExpirationPiece(req.dateExpirationPiece())
        .pourcentageCapital(req.pourcentageCapital())
        .pourcentageDroitsVote(req.pourcentageDroitsVote())
        .natureControle(req.natureControle())
        .estPep(Boolean.TRUE.equals(req.estPep()))
        .pepCategorie(req.pepCategorie())
        .pepPays(req.pepPays())
        .pepFonction(req.pepFonction())
        .presenceListesSanctions(Boolean.TRUE.equals(req.presenceListesSanctions()))
        .detailsListesSanctions(req.detailsListesSanctions())
        .build();
    e.setActif(true);

    repository.persist(e);

    auditTrail.logSimple("BeneficiaireEffectif", e.getId(), "CREATE",
        "UBO créé : " + req.prenoms() + " " + req.nom()
            + (Boolean.TRUE.equals(req.estPep()) ? " [PEP]" : "")
            + (Boolean.TRUE.equals(req.presenceListesSanctions()) ? " [SANCTIONS]" : ""));
    LOG.infof("UBO créé id=%s pour kyc=%s", e.getId(), req.kycDossierId());
    return toResponse(e);
  }

  @Transactional
  public BeneficiaireEffectifResponse mettreAJour(UUID id, UpdateBeneficiaireEffectifRequest req) {
    BeneficiaireEffectif e = repository.findById(id);
    if (e == null) throw new NotFoundException("UBO introuvable : " + id);

    if (req.pourcentageCapital() != null
        && !Objects.equals(req.pourcentageCapital(), e.getPourcentageCapital())
        && e.getKycDossier() != null) {
      verifierSeuilPourcentage(e.getKycDossier().getId(), id, req.pourcentageCapital());
    }

    if (req.nom() != null) e.setNom(req.nom());
    if (req.prenoms() != null) e.setPrenoms(req.prenoms());
    if (req.dateNaissance() != null) e.setDateNaissance(req.dateNaissance());
    if (req.lieuNaissance() != null) e.setLieuNaissance(req.lieuNaissance());
    if (req.nationalite() != null && !req.nationalite().isBlank()) e.setNationalite(req.nationalite());
    if (req.paysResidence() != null) e.setPaysResidence(req.paysResidence().isBlank() ? null : req.paysResidence());
    if (req.pourcentageCapital() != null) e.setPourcentageCapital(req.pourcentageCapital());
    if (req.pourcentageDroitsVote() != null) e.setPourcentageDroitsVote(req.pourcentageDroitsVote());
    if (req.natureControle() != null && !req.natureControle().isBlank()) e.setNatureControle(req.natureControle());
    if (req.estPep() != null) e.setEstPep(req.estPep());
    if (req.pepCategorie() != null) e.setPepCategorie(req.pepCategorie());
    if (req.pepPays() != null) e.setPepPays(req.pepPays().isBlank() ? null : req.pepPays());
    if (req.pepFonction() != null) e.setPepFonction(req.pepFonction());
    if (req.presenceListesSanctions() != null) e.setPresenceListesSanctions(req.presenceListesSanctions());
    if (req.detailsListesSanctions() != null) e.setDetailsListesSanctions(req.detailsListesSanctions());

    auditTrail.logSimple("BeneficiaireEffectif", id, "UPDATE", "Mise à jour UBO");
    return toResponse(e);
  }

  @Transactional
  public void desactiver(UUID id) {
    BeneficiaireEffectif e = repository.findById(id);
    if (e == null) throw new NotFoundException("UBO introuvable : " + id);
    e.setActif(false);
    auditTrail.logSimple("BeneficiaireEffectif", id, "DELETE", "UBO désactivé (soft-delete)");
  }

  // ────────────────────────────────────────────────────────────
  // Validations
  // ────────────────────────────────────────────────────────────

  /**
   * Vérifie que la somme des pourcentages capital sur un KycDossier ne dépasse pas 100 %.
   * Exclut l'UBO en cours de modification ({@code excludeId} = id existant à mettre à jour).
   */
  void verifierSeuilPourcentage(UUID kycDossierId, UUID excludeId, BigDecimal nouveauPourcentage) {
    if (nouveauPourcentage == null) return;
    BigDecimal cumul = repository.findByKycDossier(kycDossierId).stream()
        .filter(b -> Boolean.TRUE.equals(b.getActif()))
        .filter(b -> excludeId == null || !excludeId.equals(b.getId()))
        .map(BeneficiaireEffectif::getPourcentageCapital)
        .filter(Objects::nonNull)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
    BigDecimal total = cumul.add(nouveauPourcentage);
    if (total.compareTo(CENT) > 0) {
      throw new IllegalArgumentException(
          "Somme des pourcentages capital UBO dépasserait 100 % : " + total
              + " (cumul existant : " + cumul + ", nouveau : " + nouveauPourcentage + ")");
    }
  }

  // ────────────────────────────────────────────────────────────
  // Mapping Entity ↔ DTO
  // ────────────────────────────────────────────────────────────

  BeneficiaireEffectifResponse toResponse(BeneficiaireEffectif e) {
    return BeneficiaireEffectifResponse.builder()
        .id(e.getId())
        .kycDossierId(e.getKycDossier() != null ? e.getKycDossier().getId() : null)
        .organisationCibleId(e.getOrganisationCibleId())
        .membreId(e.getMembreId())
        .nom(e.getNom())
        .prenoms(e.getPrenoms())
        .dateNaissance(e.getDateNaissance())
        .lieuNaissance(e.getLieuNaissance())
        .nationalite(e.getNationalite())
        .paysResidence(e.getPaysResidence())
        .typePieceIdentite(e.getTypePieceIdentite())
        .numeroPieceIdentite(e.getNumeroPieceIdentite())
        .dateExpirationPiece(e.getDateExpirationPiece())
        .pourcentageCapital(e.getPourcentageCapital())
        .pourcentageDroitsVote(e.getPourcentageDroitsVote())
        .natureControle(e.getNatureControle())
        .estPep(e.isEstPep())
        .pepCategorie(e.getPepCategorie())
        .pepPays(e.getPepPays())
        .pepFonction(e.getPepFonction())
        .presenceListesSanctions(e.isPresenceListesSanctions())
        .detailsListesSanctions(e.getDetailsListesSanctions())
        .dateCreation(e.getDateCreation())
        .dateModification(e.getDateModification())
        .actif(e.getActif())
        .build();
  }
}
