package com.hpay.hpay_mobile_api.services;

import com.hpay.hpay_mobile_api.DTO.*;
import com.hpay.hpay_mobile_api.QuerySpecifications.VirementInterneSpecification;
import com.hpay.hpay_mobile_api.entities.*;
import com.hpay.hpay_mobile_api.repositories.*;
import com.hpay.hpay_mobile_api.utils.SMSapi;
import jakarta.mail.MessagingException;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;

import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;

import static com.hpay.hpay_mobile_api.utils.SendEmail.sendEmailTransfertToClientFrom;
import static com.hpay.hpay_mobile_api.utils.SendEmail.sendEmailTransfertToClientTo;

@Service
public class VirementInterneService {

    @Autowired
    private ClientService    clientService;

    @Autowired
    FCMService fcmService;

    @Autowired
    private ClientRepository clientRepository;

    @Autowired
    private CompteRepository compteRepository ;

    @Autowired
    private VirementInterneRepository virementInterneRepository ;

    @Autowired
    private JournalTransfertRepository journalTransfertRepository;

    @Autowired
    private LimiteTransfertService limiteService;

    @Autowired
    private MessageSujetRepository messageSujetRepository;

    @Autowired
    LoginAdminService loginAdminService;


    public Date getStartOfToday() {
        LocalDate today = LocalDate.now();
        return Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    public BigDecimal getTotalVirementsDuJour(Long idClientFrom) {
        Date startOfDay = getStartOfToday();
        System.out.println("Date from "+startOfDay);
        return virementInterneRepository.getTotalVirementsOfDay(idClientFrom, startOfDay);
    }

    public long getNombreVirementsDuJour(Long idClientFrom) {
        Date startOfDay = getStartOfToday();
        return virementInterneRepository.countVirementsDuJour(idClientFrom, startOfDay);
    }


    @Transactional
    public Object  saveVirement(VirementInterneDTO virementInterneDTO) {


        VirementInterne virementInterne = new VirementInterne();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMddHHmmssSSS");

        Client clientFrom = clientRepository.findById(virementInterneDTO.getIdClientFrom()).orElse(null);

        // verification que le client expéditeur (from) existe et qu'il est validé
        if (clientFrom == null || !clientFrom.getValider().equals("1") ) {
            return new ErrorResponse("client from not found", 401);  // Invalid login response
        }


        // verification que le client destinataire (to) existe et qu'il est validé
        Client clientTo = clientRepository.findById(virementInterneDTO.getIdClientTo()).orElse(null);
        //System.out.println(clientTo.getValider());
        if (clientTo == null || !clientTo.getValider().equals("1") ) {
            return new ErrorResponse("client to not found", 401);  // Invalid login response
        }


        // verification que le compte destinateur (to) existe et qu'il n'est pas suspendu ou clos
        Compte compteFrom = compteRepository.findById(virementInterneDTO.getIdCompteFrom()).orElse(null);
        if (compteFrom  == null || compteFrom.getCloture().equals("1") || compteFrom.getCompteSuspendu().equals("1") ) {
            return new ErrorResponse("compte from not found", 401);  // Invalid login response
        }


        // verification que le compte  destinataire (to) existe et qu'il n'est pas suspendu ou clos
        Compte compteTo = compteRepository.findById(virementInterneDTO.getIdCompteTo()).orElse(null);
        if (compteTo  == null  || compteTo.getCloture().equals("1") || compteTo.getCompteSuspendu().equals("1")) {
            return new ErrorResponse("compte to not found", 401);  // Invalid login response
        }


        //verification si le solde du compte emetteur est >=total
        if(compteFrom.getSolde().compareTo(virementInterneDTO.getTotal()) < 0   ){
            return new ErrorResponse("insufficient balance", 401);  // Invalid login response
        }

        //recuperation de l'objet limite global
        LimiteTransfert limitetransfertGlobal =  limiteService.findById(1).orElse(null);
        if (limitetransfertGlobal == null ) {
            return new ErrorResponse("Limite transfer not found", 401);  // Invalid login response
        }


        //verification du montant maximal par transfert
        BigDecimal montantTotalInCAD = virementInterneDTO.getTotal().multiply(virementInterneDTO.getCadRate());
        System.out.println("montant en cad "+ montantTotalInCAD);
        System.out.println("montant limite en cad "+ limitetransfertGlobal.getMontantMaxParTransfert());
        if (montantTotalInCAD.compareTo(limitetransfertGlobal.getMontantMaxParTransfert()) > 0){
            return new ErrorResponse("montant limite par transfert dépassé", 401);  // Invalid login response
        }


        //verification montant total  maximal journalier
        BigDecimal totalVirementduJour = getTotalVirementsDuJour(virementInterneDTO.getIdClientFrom()).multiply(virementInterneDTO.getCadRate()).add(montantTotalInCAD);
        System.out.println("total virement journalier en cad "+ totalVirementduJour);
        System.out.println("limite journalier "+ limitetransfertGlobal.getMontantMaxJournalier());
        if (totalVirementduJour.compareTo(limitetransfertGlobal.getMontantMaxJournalier()) >0 ){
            return new ErrorResponse("montant limite journalier depassé", 401);  // Invalid login response
        }



        //verification nombre de transferts max journalier
        long nbredevirementDuJour =  getNombreVirementsDuJour(virementInterneDTO.getIdClientFrom());
        System.out.println("nombre de virement journalier "+ nbredevirementDuJour);
        if (nbredevirementDuJour > limitetransfertGlobal.getNbTransfertsMaxJournalier() ){
            return new ErrorResponse("nombre de transfert depassé", 401);  // Invalid login response
        }


        LocalDateTime now1 = LocalDateTime.now();
        virementInterne.setVirementInterneCode(now1.format(formatter));
        LocalDateTime now2 = LocalDateTime.now();
        virementInterne.setVirementNum(now2.format(formatter));
        virementInterne.setIdClientFrom(virementInterneDTO.getIdClientFrom());
        virementInterne.setIdClientTo(virementInterneDTO.getIdClientTo());
        virementInterne.setIdCompteFrom(virementInterneDTO.getIdCompteFrom());
        virementInterne.setIdCompteTo(virementInterneDTO.getIdCompteTo());
        virementInterne.setMontant(virementInterneDTO.getMontant());
        virementInterne.setFrais(virementInterneDTO.getFrais());
        virementInterne.setFraisMontant(virementInterneDTO.getFraisMontant());
        virementInterne.setTotal(virementInterneDTO.getTotal());
        virementInterne.setDateInitiale(new Date());
        virementInterne.setIdPaysFrom(virementInterneDTO.getIdPaysFrom());
        virementInterne.setVirementRaison(virementInterneDTO.getVirementRaison());
        virementInterne.setProgrammer(virementInterneDTO.getProgrammer());
        virementInterne.setDeviseFrom(virementInterneDTO.getDeviseFrom());
        virementInterne.setDeviseTo(virementInterneDTO.getDeviseTo());
        virementInterne.setTauxConversion(virementInterneDTO.getTauxConversion());
        virementInterne.setMontantTo(virementInterneDTO.getMontantCompteTo());
        virementInterne.setMontantFrom(virementInterneDTO.getMontantCompteFrom());
        virementInterne.setGainHpay(virementInterneDTO.getMontant().multiply(virementInterneDTO.getRealRate().subtract(virementInterneDTO.getHpayRate())));
        virementInterne.setGainHpayCAD(virementInterneDTO.getMontant().multiply(virementInterneDTO.getRealRate().subtract(virementInterneDTO.getHpayRate())).multiply(virementInterneDTO.getCadRate()));
        virementInterne.setTotalConverti(virementInterneDTO.getTotal());

        // Ajout du gain Hpay dans login_Admin
        loginAdminService.ajouterSolde(virementInterneDTO.getMontant().multiply(virementInterneDTO.getRealRate().subtract(virementInterneDTO.getHpayRate())));


        // Enregistrement du virement
        virementInterne = virementInterneRepository.save(virementInterne);


        // Mise à jour du compte emétteur to
        //System.out.println(virementInterneDTO.getMontantCompteFrom());
        compteFrom.setSolde(compteFrom.getSolde().subtract(virementInterneDTO.getMontantCompteFrom()));
        compteRepository.save(compteFrom);

        // Mise à jour du compte destinataire
        //System.out.println(virementInterneDTO.getFraisMontant());
        //System.out.println(virementInterneDTO.getMontantCompteTo());
        compteTo.setSolde(compteTo.getSolde().add(virementInterneDTO.getMontantCompteTo()));
        compteRepository.save(compteTo);


        // enregistrement du log
        JournalTransfert journalTransfert = new JournalTransfert();
        journalTransfert.setAction("valider_transfert");
        journalTransfert.setStatut("Ok");
        journalTransfert.setIdclient(clientFrom.getId());
        journalTransfert.setDate_action(LocalDateTime.now());
        journalTransfert.setUser_agent(null);
        journalTransfert.setIp_client(virementInterneDTO.getIp());
        journalTransfert.setUser_agent(virementInterneDTO.getAgent());
        journalTransfertRepository.save(journalTransfert);


        // envois de la notification push au recepteur
        if(clientTo.getFcmToken() != null){
            System.out.println("envois de la notification");
            fcmService.sendNotification(
                    clientTo.getFcmToken(),
                    "Transfert d'argent reçu",
                    "Vous avez reçu un montant de "+ virementInterneDTO.getMontant()+" "+virementInterneDTO.getDeviseFrom() +" transferé par "+
                            clientFrom.getPrenoms()+" "+clientFrom.getNom()+
                            "."
            );
        }

        //enrgistrement du message
        MessageSujet messageSujet = new MessageSujet();
        messageSujet.setIdSujet(Long.valueOf(2));
        messageSujet.setIdClients(clientTo.getId());
        messageSujet.setSujet("Transfert d'argent reçu");
        messageSujet.setSujetMessage(
                "Vous avez reçu un montant de "+
                virementInterneDTO.getMontant()+
                " "+virementInterneDTO.getDeviseFrom()+
                " transferé par "+
                clientFrom.getPrenoms()+" "+clientFrom.getNom()+
                "."
        );

        messageSujet.setSujetDate(LocalDateTime.now());
        messageSujetRepository.save(messageSujet);

        LocalTime now = LocalTime.now();
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("HH:mm:ss");

        // envois email à l'expéditeur (ClientFrom)
        if(!clientFrom.getId().equals(clientTo.getId())){
            try {
                //System.out.println(clientFrom.getEmail());
                sendEmailTransfertToClientFrom(
                        clientFrom.getEmail(),
                        clientTo.getPrenoms(),
                        clientFrom.getPrenoms(),
                        virementInterneDTO.getMontantCompteFrom().toString(),
                        virementInterneDTO.getDeviseFrom(),
                        LocalDate.now(),
                        virementInterne.getVirementInterneCode(),
                        now.format(formatter1)
                );

            }catch (MessagingException e) {
                e.printStackTrace();
            }
        }


        // envois email au recepteur (ClientTo)
       if(!clientFrom.getId().equals(clientTo.getId())) {
            try {
                //System.out.println(clientTo.getEmail());
                sendEmailTransfertToClientTo(
                    clientTo.getEmail(),
                    clientTo.getPrenoms(),
                    clientFrom.getPrenoms(),
                    virementInterneDTO.getMontantCompteFrom().toString(),
                    virementInterneDTO.getDeviseTo(),
                    LocalDate.now(),
                    virementInterne.getVirementInterneCode(),
                    now.format(formatter1)
                );

            } catch (MessagingException e) {
                e.printStackTrace();
            }
       }


       // envois du sms à l'expéditeur
        SMSapi sms = new SMSapi();
        sms.alaSMS( clientFrom.getPays().getIndicatif()+ clientFrom.getTelephone(), "Hpay: vous avez transferé "+virementInterneDTO.getMontantCompteFrom().toString()+" "+virementInterne.getDeviseFrom() +
                " à " + clientTo.getPrenoms()+" le "+LocalDate.now().toString()+" à "+ now.format(formatter1)+
                ". Code:"+ virementInterne.getVirementInterneCode());


        // envois du sms au recepteur

        sms.alaSMS( clientFrom.getPays().getIndicatif()+ clientTo.getTelephone(), "Hpay: vous avez reçu "+virementInterneDTO.getMontantCompteTo().toString()+" "+virementInterne.getDeviseTo() +
                " de " + clientFrom.getPrenoms()+" le "+LocalDate.now().toString()+" à "+ now.format(formatter1)+
                ". Code: "+ virementInterne.getVirementInterneCode());


        HashMap<String, Object> map = new HashMap<>();
        map.put("data",virementInterne);

        return new SuccessResponse("le virement a été effectuer", 200, map );

    }




    public Page<VirementInterneDTO> getHistorique(
            Long idClient,
            Long idCompte,
            LocalDateTime startDate,
            LocalDateTime endDate,
            String sortDirection,

            int page,
            int size
    ) {

        Sort sort = Sort.by("dateInitiale");
        sort = "desc".equalsIgnoreCase(sortDirection) ? sort.descending() : sort.ascending();

        Pageable pageable = PageRequest.of(page, size, sort);

        Specification<VirementInterne> spec = VirementInterneSpecification.withFilters(
                idClient, idCompte, startDate, endDate
        );

        Page<VirementInterne> pageResult = virementInterneRepository.findAll(spec, pageable);

        return pageResult.map(this::mapToDTO);
    }


    private VirementInterneDTO mapToDTO(VirementInterne v) {

        VirementInterneDTO dto = new VirementInterneDTO();
        dto.setIdVirementInterne(v.getIdVirementInterne());
        dto.setMontant(v.getMontant());
        dto.setMontantTo(v.getMontantTo());
        dto.setMontantFrom(v.getMontantFrom());
        dto.setFraisMontant(v.getFraisMontant());
        dto.setTotal(v.getTotal());
        dto.setDateInitiale(v.getDateInitiale());
        dto.setVirementNum(v.getVirementNum());
        dto.setVirementRaison(v.getVirementRaison());
        dto.setDeviseFrom(v.getDeviseFrom());
        dto.setDeviseTo(v.getDeviseTo());
        dto.setTauxConversion(v.getTauxConversion());
        dto.setTotalConverti(v.getTotalConverti());
        dto.setGainHpay(v.getGainHpay());
        dto.setGainHpayCAD(v.getGainHpayCAD());
        dto.setProgrammer(v.getProgrammer());
        dto.setFrais(v.getFrais());


        clientRepository.findById(v.getIdClientFrom()).ifPresent(client -> {
            ClientDTO c = new ClientDTO();
            c.setId(client.getId());
            c.setNom(client.getNom());
            c.setPrenoms(client.getPrenoms());
            c.setSexe(client.getSexe());
            c.setPhotoClient(client.getPhotoClient());
            dto.setClientFrom(c);
        });



        clientRepository.findById(v.getIdClientTo()).ifPresent(client -> {
            ClientDTO c = new ClientDTO();
            c.setId(client.getId());
            c.setNom(client.getNom());
            c.setPrenoms(client.getPrenoms());
            c.setSexe(client.getSexe());
            c.setPhotoClient(client.getPhotoClient());
            dto.setClientTo(c);
        });


        compteRepository.findById(v.getIdCompteTo()).ifPresent(compte -> {
            CompteDTO c = new CompteDTO();
            c.setIdCompte(compte.getIdCompte());
            c.setDevise(compte.getDevise());
            c.setNumCompte(compte.getNumCompte());
            dto.setCompteTo(c);
        });


        compteRepository.findById(v.getIdCompteFrom()).ifPresent(compte -> {
            CompteDTO c = new CompteDTO();
            c.setIdCompte(compte.getIdCompte());
            c.setDevise(compte.getDevise());
            c.setNumCompte(compte.getNumCompte());
            dto.setCompteFrom(c);
        });

        return dto;
    }


}
