package com.avitam.bankloanapplication.service.impl;

import com.avitam.bankloanapplication.model.dto.LoanDetailsDto;
import com.avitam.bankloanapplication.model.dto.LoanDetailsWsDto;
import com.avitam.bankloanapplication.model.dto.LoanEmiDetailDto;
import com.avitam.bankloanapplication.model.entity.Customer;
import com.avitam.bankloanapplication.model.entity.Loan;
import com.avitam.bankloanapplication.model.entity.LoanApplication;
import com.avitam.bankloanapplication.model.entity.LoanDetails;
import com.avitam.bankloanapplication.model.entity.LoanTemplate;
import com.avitam.bankloanapplication.pdfGenerator.PdfGenerator;
import com.avitam.bankloanapplication.repository.CustomerRepository;
import com.avitam.bankloanapplication.repository.LoanApplicationRepository;
import com.avitam.bankloanapplication.repository.LoanDetailsRepository;
import com.avitam.bankloanapplication.repository.LoanRepository;
import com.avitam.bankloanapplication.repository.LoanTemplateRepository;
import com.avitam.bankloanapplication.service.LoanDetailsService;
import com.avitam.bankloanapplication.service.NotificationService;
import com.avitam.bankloanapplication.web.controllers.ErrorHandlerController;
import jakarta.activation.DataHandler;
import jakarta.activation.DataSource;
import jakarta.mail.Authenticator;
import jakarta.mail.Message;
import jakarta.mail.Multipart;
import jakarta.mail.PasswordAuthentication;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.util.ByteArrayDataSource;
import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

@Service
public class LoanDetailsServiceImpl implements LoanDetailsService {

    public static final String ADMIN_LOANDETAILS = "/loans/loanDetails";
    Logger LOG = LoggerFactory.getLogger(ErrorHandlerController.class);
    @Value("${spring.mail.username}")
    private String username;
    @Value("${spring.mail.password}")
    private String password;
    @Autowired
    private LoanDetailsRepository loanDetailsRepository;
    @Autowired
    private LoanRepository loanRepository;
    @Autowired
    private LoanTemplateRepository loanTemplateRepository;
    @Autowired
    private ModelMapper modelMapper;
    @Autowired
    private CustomerRepository customerRepository;

    @Autowired
    private NotificationService notificationService;
    @Autowired
    private LoanApplicationRepository loanApplicationRepository;
    @Autowired
    private PdfGenerator pdfGenerator;

    @Override
    public LoanDetailsWsDto createLoanDetailsForLoan(LoanDetailsDto loanDetailsDto, LoanApplication loanApplication) {
        List<LoanDetails> loanDetailsList = new ArrayList<>();
        LoanDetailsWsDto request = new LoanDetailsWsDto();
        Customer customer = customerRepository.findByRecordId(loanApplication.getCustomerId());
        LoanDetails loanDetails;
        if (loanDetailsDto.getRecordId() != null) {
            loanDetails = loanDetailsRepository.findByRecordId(loanDetailsDto.getRecordId());
            calculateLoanDetailsForLoan(loanDetails);
            totalAmountCalculationForLoan(loanDetails);
            loanDetailsRepository.save(loanDetails);
            modelMapper.map(loanDetailsDto, loanDetails);
        } else {
            loanDetails = loanDetailsRepository.findByLoanId(loanDetailsDto.getLoanId());
            if (loanDetails == null) {
                loanDetails = modelMapper.map(loanDetailsDto, LoanDetails.class);
                loanDetails.setCreationTime(new Date());
                loanDetails.setStatus(true);
                loanDetailsRepository.save(loanDetails);
            }

            calculateLoanDetailsForLoan(loanDetails);
            totalAmountCalculationForLoan(loanDetails);
        }

        if (loanDetails.getRecordId() == null) {
            loanDetails.setRecordId(String.valueOf(loanDetails.getId().getTimestamp()));
        }
        loanDetailsRepository.save(loanDetails);
        Loan loan = loanRepository.findByRecordId(loanDetails.getLoanId());
        loan.setLoanEmiDetailDtoList(loanDetails.getLoanDetailsDtoList());
        loan.setLoanEmi(loanDetails.getLoanDetailsDtoList().get(0).getTotalPayable());
        loanRepository.save(loan);
        if (customer != null && customer.getNotificationId() != null) {
            notificationService.sendNotificationToCustomer(
                    "Loan Approved",
                    "Hurray!!, your loan - " + loanApplication.getLoanId() + " has been approved",
                    customer.getNotificationId());
        }
        loanDetailsList.add(loanDetails);

        Type listType = new TypeToken<List<LoanDetailsDto>>() {
        }.getType();
        List<LoanDetailsDto> dtoList = modelMapper.map(loanDetailsList, listType);
        request.setLoanDetailsDtos(dtoList);
        sendEmail(customer.getEmail(), loan);
        return request;
    }

    private void sendEmail(String recipientEmail, Loan loan) {
        try {
            Properties properties = new Properties();
            properties.put("mail.transport.protocol", "smtp");  // Protocol (SMTP)
            properties.put("mail.smtp.auth", "true");           // Enable authentication
            properties.put("mail.smtp.starttls.enable", "true"); // Use TLS
            properties.put("mail.smtp.host", "smtp.gmail.com");  // SMTP Host
            properties.put("mail.smtp.port", "587");             // SMTP Port
            properties.put("mail.smtp.ssl.trust", "*");
            properties.put("mail.debug", "true");

            String loanId = loan.getRecordId();
            Session session = Session.getInstance(properties, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);  // Use your email and app password
                }
            });

            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(username));
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail));

            Multipart multipart = new MimeMultipart();
            MimeBodyPart textPart = new MimeBodyPart();
            textPart.setText("Please find the attached PDF document.");
            multipart.addBodyPart(textPart);

            ByteArrayOutputStream loanAgreementPdf = pdfGenerator.loanAggrementPdf(loanId);
            ByteArrayOutputStream annexureBPdf = pdfGenerator.annexureBPdf(loanId);

            MimeBodyPart pdfPart = new MimeBodyPart();
            DataSource dataSource = new ByteArrayDataSource(loanAgreementPdf.toByteArray(), "application/pdf");
            pdfPart.setDataHandler(new DataHandler(dataSource));
            pdfPart.setFileName(loanId + "_LoanAgreement.pdf");
            multipart.addBodyPart(pdfPart);

            MimeBodyPart pdfPart2 = new MimeBodyPart();
            DataSource dataSource2 = new ByteArrayDataSource(annexureBPdf.toByteArray(), "application/pdf");
            pdfPart2.setDataHandler(new DataHandler(dataSource2));
            pdfPart2.setFileName(loanId + "_ Annexure.pdf");
            multipart.addBodyPart(pdfPart2);

            message.setContent(multipart);
            Transport.send(message);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }


//    @Override
//    public LoanDetailsWsDto createLoanDetailsForLoanTemplate(LoanDetailsWsDto request) {
//        List<LoanDetailsDto> loanDetailsDtos = request.getLoanDetailsDtos();
//        List<LoanDetails> loanDetailsList = new ArrayList<>();
//        for (LoanDetailsDto loanDetailsDto : loanDetailsDtos) {
//            LoanDetails loanDetails;
//            if (loanDetailsDto.getRecordId() != null || loanDetailsDto.getLoanId()!=null) {
//                loanDetails = loanDetailsRepository.findByRecordId(loanDetailsDto.getRecordId());
//                calculateLoanDetails(loanDetails);
//                totalAmountCalculation(loanDetails);
//                loanDetailsRepository.save(loanDetails);
//                modelMapper.map(loanDetailsDto, loanDetails);
//                request.setMessage("Data updated successfully");
//            } else {
//                loanDetails = loanDetailsRepository.findByLoanId(loanDetailsDto.getLoanId());
//                if (loanDetails == null) {
//                    loanDetails = modelMapper.map(loanDetailsDto, LoanDetails.class);
//                    loanDetails.setCreationTime(new Date());
//                    loanDetails.setStatus(true);
//                    calculateLoanDetails(loanDetails);
//                    totalAmountCalculation(loanDetails);
//                    loanDetailsRepository.save(loanDetails);
//                }
//
//                request.setMessage("Data added successfully");
//            }
//
//            if (request.getRecordId() == null) {
//                loanDetails.setRecordId(String.valueOf(loanDetails.getId().getTimestamp()));
//            }
//            loanDetailsRepository.save(loanDetails);
//            LoanTemplate loanTemplate = loanTemplateRepository.findByRecordId(loanDetails.getLoanId());
//            loanTemplate.setLoanEmiDetailDtoList(loanDetails.getLoanDetailsDtoList());
//            for (LoanEmiDetailDto loanEmiDetailDto : loanDetails.getLoanDetailsDtoList()) {
//                loanTemplate.setLoanEmi(loanEmiDetailDto.getTotalPayable());
//                break;
//            }
//            loanTemplateRepository.save(loanTemplate);
//            loanDetailsList.add(loanDetails);
//        }
//
//        Type listType = new TypeToken<List<LoanDetailsDto>>() {
//        }.getType();
//        List<LoanDetailsDto> dtoList = modelMapper.map(loanDetailsList, listType);
//        request.setLoanDetailsDtos(dtoList);
//        request.setBaseUrl(ADMIN_LOANDETAILS);
//
//        return request;
//    }

    @Override
    public LoanDetailsWsDto createLoanDetailsForLoanTemplate(LoanDetailsWsDto request) {
        List<LoanDetailsDto> loanDetailsDtos = request.getLoanDetailsDtos();
        List<LoanDetails> loanDetailsList = new ArrayList<>();


        for (LoanDetailsDto loanDetailsDto : loanDetailsDtos) {
            LoanDetails loanDetails;


            if (loanDetailsDto.getRecordId() != null || loanDetailsDto.getLoanId() != null) {
                loanDetails = loanDetailsRepository.findByRecordId(loanDetailsDto.getRecordId());


                if (loanDetails != null) {
                    calculateLoanDetails(loanDetails);
                    totalAmountCalculation(loanDetails);
                    loanDetailsRepository.save(loanDetails);
                    modelMapper.map(loanDetailsDto, loanDetails);
                    request.setMessage("Data updated successfully");
                } else {
                    loanDetails = modelMapper.map(loanDetailsDto, LoanDetails.class);
                    loanDetails.setCreationTime(new Date());
                    loanDetails.setStatus(true);
                    calculateLoanDetails(loanDetails);
                    totalAmountCalculation(loanDetails);
                    loanDetailsRepository.save(loanDetails);
                    request.setMessage("Record not found, created new LoanDetails");
                }
            } else {
                loanDetails = loanDetailsRepository.findByLoanId(loanDetailsDto.getLoanId());
                if (loanDetails == null) {
                    loanDetails = modelMapper.map(loanDetailsDto, LoanDetails.class);
                    loanDetails.setCreationTime(new Date());
                    loanDetails.setStatus(true);
                    calculateLoanDetails(loanDetails);
                    totalAmountCalculation(loanDetails);
                    loanDetailsRepository.save(loanDetails);
                }
                request.setMessage("Data added successfully");
            }


            if (loanDetails.getRecordId() == null) {
                loanDetails.setRecordId(String.valueOf(loanDetails.getId().getTimestamp()));
            }


            loanDetailsRepository.save(loanDetails);
            LoanTemplate loanTemplate = loanTemplateRepository.findByRecordId(loanDetails.getLoanId());
            if (loanTemplate != null) {
                loanTemplate.setLoanEmiDetailDtoList(loanDetails.getLoanDetailsDtoList());
                for (LoanEmiDetailDto loanEmiDetailDto : loanDetails.getLoanDetailsDtoList()) {
                    loanTemplate.setLoanEmi(loanEmiDetailDto.getTotalPayable());
                    break;
                }
                loanTemplateRepository.save(loanTemplate);
            }


            loanDetailsList.add(loanDetails);
        }


        Type listType = new TypeToken<List<LoanDetailsDto>>() {
        }.getType();
        List<LoanDetailsDto> dtoList = modelMapper.map(loanDetailsList, listType);
        request.setLoanDetailsDtos(dtoList);
        request.setBaseUrl(ADMIN_LOANDETAILS);


        return request;
    }

    public LoanDetails calculateLoanDetails(LoanDetails loanDetails) {
        LoanTemplate loanTemplate = loanTemplateRepository.findByRecordId(loanDetails.getLoanId());

        BigDecimal totalLoanAmount = loanTemplate.getDesiredLoan();
        //  BigDecimal installment = loanTemplate.getDesiredLoan().divide(loanTemplate.getTenure());
        BigDecimal installment = loanTemplate.getDesiredLoan().divide(loanTemplate.getTenure(), 2, RoundingMode.HALF_UP);

        BigDecimal interestRate = loanTemplate.getInterestRate();
        BigDecimal interestAmount;
        BigDecimal emi;


        List<LoanEmiDetailDto> loanDetailsDtoList = new ArrayList<>();
        List<LocalDate> duedatesList = new ArrayList<>();

        for (int i = 0; i < loanTemplate.getTenure().intValue(); i++) {
            LoanEmiDetailDto detail = new LoanEmiDetailDto();

            interestAmount = totalLoanAmount.multiply(interestRate.divide(BigDecimal.valueOf(100)));
            emi = installment.add(interestAmount);
            totalLoanAmount = totalLoanAmount.subtract(installment);

            detail.setInstalment(roundToTwoDecimal(installment));
            detail.setInterestAmount(roundToTwoDecimal(interestAmount));
            detail.setTotalPayable(roundToTwoDecimal(emi));
            detail.setPenalty(BigDecimal.valueOf(0));
            detail.setPaymentStatus("Unpaid");
            detail.setLoanPaidDate(null);
            detail.setRecordId(String.valueOf(i));

            loanDetailsDtoList.add(detail);
        }
        loanDetails.setLoanDetailsDtoList(loanDetailsDtoList);
        return loanDetails;
    }

    public LoanDetails calculateLoanDetailsForLoan(LoanDetails loanDetails) {
        Loan loan = loanRepository.findByRecordId(loanDetails.getLoanId());

        BigDecimal totalLoanAmount = loan.getDesiredLoan();
        LocalDate sanctionDate = loan.getSanctionDate();

        BigDecimal installment = loan.getDesiredLoan().divide(loan.getTenure(), 2, RoundingMode.HALF_UP);

        BigDecimal interestRate = loan.getInterestRate();
        BigDecimal interestAmount;
        BigDecimal emi;


        List<LoanEmiDetailDto> loanDetailsDtoList = new ArrayList<>();
        List<LocalDate> duedatesList = new ArrayList<>();

        LocalDate baseDate = sanctionDate.withDayOfMonth(5);
        LocalDate currentDate = LocalDate.now();
        int noOfMonths = (int) ChronoUnit.MONTHS.between(baseDate, currentDate);

        if (sanctionDate.getDayOfMonth() > 5) {
            baseDate = baseDate.plusMonths(1);
        } else {
            baseDate = baseDate.plusMonths(0);
        }

        for (int i = 0; i < loan.getTenure().intValue(); i++) {
            LoanEmiDetailDto detail = new LoanEmiDetailDto();

            interestAmount = totalLoanAmount.multiply(interestRate.divide(BigDecimal.valueOf(100)));
            emi = installment.add(interestAmount);
            totalLoanAmount = totalLoanAmount.subtract(installment);
            LocalDate dueDate = baseDate.plusMonths(i);

            detail.setInstalment(roundToTwoDecimal(installment));
            detail.setInterestAmount(roundToTwoDecimal(interestAmount));
            detail.setTotalPayable(roundToTwoDecimal(emi));
            detail.setPenalty(BigDecimal.valueOf(0));
            detail.setPaymentStatus("Unpaid");
            detail.setDueDate(dueDate);
            detail.setLoanPaidDate(null);
            detail.setRecordId(String.valueOf(i));

            loanDetailsDtoList.add(detail);
        }

        loanDetails.setLoanDetailsDtoList(loanDetailsDtoList);

        return loanDetails;
    }

    public LoanDetails totalAmountCalculationForLoan(LoanDetails loanDetails) {
        BigDecimal totalInterestAmount = BigDecimal.ZERO;
        BigDecimal totalInstalmentAmount = BigDecimal.ZERO;
        BigDecimal totalPayableAmount = BigDecimal.ZERO;

        Loan loan = loanRepository.findByRecordId(loanDetails.getLoanId());

        for (LoanEmiDetailDto loanDetailDto : loanDetails.getLoanDetailsDtoList()) {
            totalInterestAmount = totalInterestAmount.add(loanDetailDto.getInterestAmount());
            totalInstalmentAmount = totalInstalmentAmount.add(loanDetailDto.getInstalment());
            totalPayableAmount = totalPayableAmount.add(loanDetailDto.getTotalPayable());
        }

        loanDetails.setTotalInterestAmount(totalInterestAmount);
        loanDetails.setTotalInstalmentAmount(roundToTwoDecimal(totalInstalmentAmount));
        loanDetails.setTotalPayableAmount(roundToTwoDecimal(totalPayableAmount));

        loan.setPendingInstallmentAmount(roundToTwoDecimal(totalInstalmentAmount));
        loanRepository.save(loan);

        return loanDetails;
    }


    public LoanDetails totalAmountCalculation(LoanDetails loanDetails) {
        BigDecimal totalInterestAmount = BigDecimal.valueOf(0.0);
        BigDecimal totalInstalmentAmount = BigDecimal.valueOf(0.0);
        BigDecimal totalPayableAmount = BigDecimal.valueOf(0.0);

        for (LoanEmiDetailDto loanDetailDto : loanDetails.getLoanDetailsDtoList()) {
            totalInterestAmount = totalInterestAmount.add(loanDetailDto.getInterestAmount());
            totalInstalmentAmount = totalInstalmentAmount.add(loanDetailDto.getInstalment());
            totalPayableAmount = totalPayableAmount.add(loanDetailDto.getTotalPayable());
        }

        loanDetails.setTotalInterestAmount(roundToTwoDecimal(totalInterestAmount));
        loanDetails.setTotalInstalmentAmount(roundToTwoDecimal(totalInstalmentAmount));
        loanDetails.setTotalPayableAmount(roundToTwoDecimal(totalPayableAmount));

        return loanDetails;
    }


    private BigDecimal roundToTwoDecimal(BigDecimal value) {
        return value.setScale(2, RoundingMode.HALF_UP);
    }

}