package com.sc.sicanet.migracion_sicanet.service;

import com.sc.sicanet.migracion_sicanet.DTO.PrestamosSolicitudesDTO;
import com.sc.sicanet.migracion_sicanet.entity.*;
import com.sc.sicanet.migracion_sicanet.repository.*;
import com.sc.sicanet.migracion_sicanet.utils.Funciones;
import jakarta.persistence.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;

import java.sql.Date;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

@Service
public class PrestamosSolicitudesServiceImpl implements PrestamosSolicitudesService {
    @Autowired
    private PrestamosSolicitudesRepository prestamosSolicitudesRepository;
    @Autowired
    private CatTipoPrestamosRepository catTipoPrestamosRepository;
    @Autowired
    private CatBanxicoTiieRepository catBanxicoTiieRepository;
    @Autowired
    private ConfiguracionEmpresaRepository configuracionEmpresaRepository;
    @Autowired
    private CatTipoPrestamosPeriodosRepository catTipoPrestamosPeriodosRepository;
    @Autowired
    private Funciones funciones;

    private static final int longitud_consecutivo = 7;

    @PersistenceContext
    private EntityManager entityManager;

    public PrestamosSolicitudes guardarPrestamosSolicitudes(PrestamosSolicitudes prestamosSolicitudes) {
        return prestamosSolicitudesRepository.save(prestamosSolicitudes);
    }

    @Override
    public Optional<PrestamosSolicitudes> consultaPrestamosSolicitudes(Socio socio) {
        return prestamosSolicitudesRepository.findBySocio(socio);
    }

    public Optional<PrestamosSolicitudes> consultaSolicitudesByControlAndEstatus(String control_solicitud, String estatus){
        return prestamosSolicitudesRepository.findByControlAndEstatus(control_solicitud, estatus);
    }

    /*
     * @autor Metzli Amaya
     * Regresa el número de control de la solicitud
     * Por Ejemplo: 001-SOL-1900001 que indica:
     * 001 Número de la Sucursal
     * SOL Solicitud
     * 25 Indica el año de la solicitud
     * 00001 Indica el número consecutivo por año y por sucursal
     *
     * @param fecha_solicitud Fecha de solicitud
     * @param sucursal
     * @return control_solicitud
     */
    public String controlSolicitud(LocalDate fechaSolicitud, int sucursal) {
        String controlSolicitud = "";
        String fechaSolicitud_ = String.valueOf(fechaSolicitud.getYear()).substring(2, 4);
        int consecutivo = prestamosSolicitudesRepository.obtenerConsecutivo(fechaSolicitud.getYear(), sucursal);
        controlSolicitud = "0001";
        controlSolicitud += "-SOL-";
        controlSolicitud += fechaSolicitud_;
        controlSolicitud += funciones.LPAD(consecutivo, "0", longitud_consecutivo - 2);
        return controlSolicitud;
    }

    /*
     * @autor Metzli Amaya
     * Calcula las tasas de interes, normal y moratorio del tipo de prestamo indicado
     *
     * @param catTipoPrestamos
     * @param tiie - valor de la tasa tiie
     * @param multiplicadorInteresMoratorio 
     * @return tasas
     */

    private Map<String, Double> calcularTasas(Optional<CatTipoPrestamos> catTipoPrestamos, Optional<CatBanxicoTiie> tiie, Optional<ConfiguracionEmpresa> multiplicadorInteresMoratorio) {
        int multiplicador_tasa_normal = multiplicadorInteresMoratorio.map(ConfiguracionEmpresa::getMultiplicadorInteresMoratorio).orElse(0);
        Double porcentaje_anual_tiie = catTipoPrestamos.map(CatTipoPrestamos::getPorcentajeAnualTiie).orElse(0.0);
        Double valor_tiie = tiie.map(CatBanxicoTiie::getTasa).orElse(0.0);
        Double tasa_normal = (valor_tiie == 0) ? catTipoPrestamos.map(CatTipoPrestamos::getIntn).orElse(0.0) : porcentaje_anual_tiie + valor_tiie;
        Double intm = tasa_normal * multiplicador_tasa_normal;

        Map<String, Double> tasas = new HashMap<>();
        tasas.put("tasa_normal", tasa_normal);
        tasas.put("intm", intm);
        return tasas;
    }

    public PrestamosSolicitudes convertirDatosDePrestamosSolicitudes(PrestamosSolicitudesDTO prestamosSolicitudesDTO, Socio socio) {
        LocalDateTime fechaHoraActual = LocalDateTime.now();
        LocalDate fechaActual = LocalDate.now();
        Optional<CatBanxicoTiie> tiie = catBanxicoTiieRepository.findFirstByOrderByFechaDesc();
        Optional<ConfiguracionEmpresa> multiplicadorInteresMoratorio = configuracionEmpresaRepository.findFirstBy();
        Optional<CatTipoPrestamos> catTipoPrestamos = catTipoPrestamosRepository.findByPkTipoPrestamo(prestamosSolicitudesDTO.getTipo_solicitud());
        if(!catTipoPrestamos.isPresent()) throw new IllegalArgumentException("Tipo De Préstamo No Válido.");
        int tipoSolicitud = prestamosSolicitudesDTO.getTipo_solicitud();
        if (tipoSolicitud == 10 || tipoSolicitud == 14) throw new IllegalArgumentException("El Tipo De Préstamo Ingresado No Está Permitido.");
        Optional<CatTipoPrestamosPeriodos> periodicidad = Optional.empty();
        try {
            String acronimo = prestamosSolicitudesDTO.getPeriodicidad().trim().toUpperCase();
            periodicidad = catTipoPrestamosPeriodosRepository.findByFkTipoPrestamoAndAcronimoPeriodoAndEstatus(catTipoPrestamos.get().getPkTipoPrestamo(), acronimo, "A");
        } catch (RuntimeException e) {
            System.out.println("erorr -> " + e);
        }
        if(!periodicidad.isPresent()) throw new IllegalArgumentException("Periodicidad No Válida Para Este Tipo De Préstamo.");
        String periodicidadCorrecta = periodicidad.get().getAcronimoPeriodo();
        int numeroPagares = prestamosSolicitudesDTO.getPagares();
        if (numeroPagares < periodicidad.get().getPlazoMinimo() || numeroPagares > periodicidad.get().getPlazoMaximo())
            throw new IllegalArgumentException("El Número De Pagos Ingresado No Es Válido.");
        Double montoSolicitado = prestamosSolicitudesDTO.getMonto_solicitado();
        Double montoMinimo = catTipoPrestamos.map(CatTipoPrestamos::getMontoMinimo).orElse(0.0);
        Double montoMaximo = catTipoPrestamos.map(CatTipoPrestamos::getMontoMaximo).orElse(0.0);
        if (montoSolicitado < montoMinimo || montoSolicitado > montoMaximo)
            throw new IllegalArgumentException("El Monto Solicitado No Es Válido Para Este Tipo De Préstamo.");
        Map<String, Double> tasas = calcularTasas(catTipoPrestamos, tiie, multiplicadorInteresMoratorio);
        Double tasa_normal = tasas.get("tasa_normal");
        Double intm = tasas.get("intm");
        String control = controlSolicitud(fechaActual, 1);
        Boolean es_promocion = catTipoPrestamos.map(ctp -> "S".equalsIgnoreCase(ctp.getPromocion())).orElse(false);
        int meses_promocion = es_promocion ? catTipoPrestamos.map(CatTipoPrestamos::getMesesPromocion).orElse(0) : 0;

        prestamosSolicitudesDTO.setInteres_normal(tasa_normal);
        prestamosSolicitudesDTO.setInteres_moratorio(intm);

        PrestamosSolicitudes prestamosSolicitudes = new PrestamosSolicitudes(fechaHoraActual,  Date.valueOf("1990-01-01"),"I","N","N",
                "API MIGRACION","P", Date.valueOf("1990-01-01"), Date.valueOf("1990-01-01"), Date.valueOf("1990-01-01"), Date.valueOf("1990-01-01"),
                Date.valueOf("1990-01-01"),0,0,1,0,0,0,
                0,0,0, catTipoPrestamos.isPresent() ? catTipoPrestamos.get().getPkTipoPrestamo() : 0,0.0,
                montoSolicitado,0.0,0.0, prestamosSolicitudesDTO.getIncluir_iva(),0,"","",
                "", numeroPagares,0, periodicidadCorrecta,"",0.0,"N","","",
                1, prestamosSolicitudesDTO.getTipo_cobro(), 0.0);

        prestamosSolicitudes.setFechaControl(fechaHoraActual);
        prestamosSolicitudes.setFechaRegistro(fechaHoraActual);
        prestamosSolicitudes.setFechaSolicitud(fechaActual);
        prestamosSolicitudes.setControl(control);

        if(prestamosSolicitudesDTO.getControl_solicitud() != null && !prestamosSolicitudesDTO.getControl_solicitud().isEmpty()) {
            Optional<PrestamosSolicitudes> prestamoSolicitudExistente = consultaSolicitudesByControlAndEstatus(prestamosSolicitudesDTO.getControl_solicitud(), "P");
            if (prestamoSolicitudExistente.isPresent()) {
                prestamosSolicitudes.setPkPrestamoSolicitud(prestamoSolicitudExistente.get().getPkPrestamoSolicitud());
                prestamosSolicitudes.setFechaControl(prestamoSolicitudExistente.get().getFechaControl());
                prestamosSolicitudes.setFechaRegistro(prestamoSolicitudExistente.get().getFechaRegistro());
                prestamosSolicitudes.setFechaSolicitud(prestamoSolicitudExistente.get().getFechaSolicitud());
                prestamosSolicitudes.setControl(prestamoSolicitudExistente.get().getControl());
            }
        }
        return prestamosSolicitudes;
    }
}