/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package com.arrebol.taxiservicios.beans.predio;

import com.arrebol.taxiservicios.beans.TaxiGenericBean;
import com.arrebol.taxiservicios.controller.admin.AprobarCancelacionController;
import com.arrebol.taxiservicios.controller.admin.CustomerController;
import com.arrebol.taxiservicios.controller.admin.GenericValidationController;
import com.arrebol.taxiservicios.controller.cobranza.CobranzaController;
import com.arrebol.taxiservicios.controller.predio.AbonoLoteController;
import com.arrebol.taxiservicios.controller.predio.BucketController;
import com.arrebol.taxiservicios.controller.predio.CambiaLoteController;
import com.arrebol.taxiservicios.controller.predio.DevolucionAbonoController;
import com.arrebol.taxiservicios.controller.predio.PredioController;
import com.arrebol.taxiservicios.controller.predio.PreventaController;
import com.arrebol.taxiservicios.model.catalog.Location;
import com.arrebol.taxiservicios.model.core.ContactNumber;
import com.arrebol.taxiservicios.model.core.Person;
import com.arrebol.taxiservicios.model.enums.EstatusLote;
import com.arrebol.taxiservicios.model.enums.EstatusRembolso;
import com.arrebol.taxiservicios.model.enums.EstatusSolicitud;
import com.arrebol.taxiservicios.model.enums.GenericEnumType;
import com.arrebol.taxiservicios.model.enums.PeriodoPago;
import com.arrebol.taxiservicios.model.enums.PersonType;
import com.arrebol.taxiservicios.model.enums.TipoAbono;
import com.arrebol.taxiservicios.model.predio.AbonoLote;
import com.arrebol.taxiservicios.model.predio.AbonoLoteView;
import com.arrebol.taxiservicios.model.predio.Lote;
import com.arrebol.taxiservicios.model.predio.Manzana;
import com.arrebol.taxiservicios.model.predio.Predio;
import com.arrebol.taxiservicios.model.predio.RembolsosCancelacionCompraLote;
import com.arrebol.taxiservicios.model.predio.SolicitudDevolucionAbono;
import com.arrebol.taxiservicios.model.predio.SolicitudTrasferenciaLote;
import com.arrebol.taxiservicios.model.predio.SolicitudesCancelacionCompraLote;
import com.arrebol.taxiservicios.model.view.PersonListView;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.imageio.ImageIO;
import javax.inject.Named;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.primefaces.context.RequestContext;

/**
 *
 * @author pc one
 */
@Named("preventaBean")
@ViewScoped
public class PreventaBean extends TaxiGenericBean implements Serializable {

    private static final long serialVersionUID = -7284027390656669184L;
    final Logger logger = LogManager.getLogger(getClass());
    private PredioController predioController;
    private PreventaController preventaController;
    private AbonoLoteController abonoLoteController;
    private AprobarCancelacionController aprobarCancelacionController;
    private CambiaLoteController cambiaLoteController;
    private DevolucionAbonoController devolucionAbonoController;
    private CobranzaController cobranzaController;
    private List<String> valoresFiltro = new ArrayList<>();
    private String valorFiltroSeleccionado;
    private CustomerController controladorCustomer;
    private CustomerController customerController;
    private List<AbonoLote> abonos;
    private List<Predio> predios;
    private List<Manzana> manzanas;
    private List<Lote> lotes;
    private Lote selectedLote;
    private String periodoPago;
    List<PersonListView> listClient;
    private String selectedClient;
    private String tipoActionLote;
    private String activeTap;
    private Boolean activeInput;
    private double actualizarPago;
    private Boolean tipoPago;
    private Boolean tipoAbono;
    private Person customer;
    private JasperPrint print;
    private Predio predioConstruido;
    private Boolean active;
    private String imagenMostrar;
    private String comboPreventaActive;
    private Boolean active2;
    private String comentario;
    private Double descuentoRembolso = 0.0;
    private Double AnticipoReserva;
    private Boolean pagoExtra;
    private Date fechaPagoExtra;
    private Double cantidadPagoExtra;
    private Boolean valGenerarPagos;
    private Boolean validadorBotonImagen;
    private BucketController controladorBucket;
    
    private GenericValidationController genericController;

    @PostConstruct
    public void init() {
        logger.info("start  init preventaBean");
        try {
            setControladorBucket(new BucketController());
            setActive(false);
            setValidadorBotonImagen(false);
            setActive2(true);
            valoresFiltro.add("Todos");
            valoresFiltro.add("Solo disponibles");
            comboPreventaActive = "null";
            valorFiltroSeleccionado = valoresFiltro.get(0);
            loadBundlePropertyFile();
            setPreventaController(new PreventaController());
            setCustomer(new Person(PersonType.CUSTOMER));
            setCustomerController(new CustomerController());
            setPredios(getPreventaController().fillAllPredio(getLoggedUser().getLocation()));
            setListClient(getPreventaController().findListOfPersonsByType(PersonType.CUSTOMER, getLoggedUser().getLocation()));
            setActiveTap("0");
        } catch (Exception e) {
            logger.error("Preventa init", e);
        }
    }

    public void verImagen() {

        if (getValidadorBotonImagen() != null) {
            File documento = getControladorBucket().utilizarBucket(getBundlePropertyFile().getString("aws.access.key"), getBundlePropertyFile().getString("aws.secret.access.key"), getBundlePropertyFile().getString("aws.region"), getBundlePropertyFile().getString("aws.bucket"), getPredioConstruido(), 1, null, getPredioConstruido().getCroquis());
            String[] nombreDoc = getPredioConstruido().getCroquis().split("\\.", 2);
            if (documento != null && documento.exists()) {
                try {
                    byte[] imageBytes = new byte[(int) documento.length()];
                    FileInputStream fileInputStream = new FileInputStream(documento);
                    fileInputStream.read(imageBytes);
                    fileInputStream.close();

                    String base64Image = Base64.getEncoder().encodeToString(imageBytes);
                    setImagenMostrar("data:image/" + nombreDoc[1] + ";base64," + base64Image);
                } catch (IOException e) {
                    // Manejo de errores
                }
            } else {
                // Manejo de errores (archivo no encontrado)
            }

        }
    }

    public void cambiosListaFormulario() {
        System.out.println(getValorFiltroSeleccionado());
        if (getValorFiltroSeleccionado().equals("Todos")) {
            setPredios(getPreventaController().fillAllPredio(getLoggedUser().getLocation()));
            setListClient(getPreventaController().findListOfPersonsByType(PersonType.CUSTOMER, getLoggedUser().getLocation()));
            comboPreventaActive = "null";
        } else if (getValorFiltroSeleccionado().equals("Solo disponibles")) {
            setPredios(getPreventaController().fillAllPredio(getLoggedUser().getLocation()));
            Integer validadorManzanaLote = 0;
            comboPreventaActive = "null";
            for (int i = 0; i <= getPredios().size() - 1; i++) {
                long consulta = getPreventaController().countManzanasByProyecto(getPredios().get(i).getId());
                if (consulta == 0) {
                    getPredios().remove(i);
                    i--;
                } else {
                    List<Manzana> listaManzanasPorProyecto = getPreventaController().fillAllManzanas(getPredios().get(i));
                    for (int j = 0; j <= listaManzanasPorProyecto.size() - 1; j++) {
                        long valor = getPreventaController().countLotesByManzana(listaManzanasPorProyecto.get(j).getId());
                        if (valor == 0) {
                            listaManzanasPorProyecto.remove(j);
                            j--;
                        }
                    }
                    if (listaManzanasPorProyecto.size() == 0) {
                        getPredios().remove(i);
                        i--;
                    }
                }
            }
        }

    }

    public void addCustomer() {
        logger.info("addCustomer");
        try {

            getCustomer().setId(UUID.randomUUID().toString());
            getCustomer().setActive(true);
            getCustomer().setRoot(false);
            getCustomer().setCreatedOn(new Date());
            getCustomer().setLocation(new Location(getLoggedUser().getLocation().getId()));

            boolean success = getCustomerController().addPerson(getCustomer(), getLoggedUser().getUserName(), true);

            if (success) {

                showMessage(FacesMessage.SEVERITY_INFO, "Cancelado", "El cliente fue agregado de manera correcta.");
                setListClient(getPreventaController().findListOfPersonsByType(PersonType.CUSTOMER, getLoggedUser().getLocation()));
            }
        } catch (Exception e) {
            logger.error("addEmployee: " + e);
            showMessage(FacesMessage.SEVERITY_FATAL,
                    getBundlePropertyFile().getString("customer.add.title"),
                    getBundlePropertyFile().getString("customer.add.exception.desc"));
        }
    }

    public void updateLotes(String idManzana) {
        
        // Se verifica el fin de la reserva de los lotes aparados
        List<Lote> listaLo = getPreventaController().fillAllLotes(new Manzana(idManzana));            
        for (Lote listaL : listaLo) {
            if(listaL.getEstatusLote().equals(EstatusLote.APARTADO)){
                if(new Date().after(listaL.getFinReserva()) || new Date().equals(listaL.getFinReserva())){
                    cancelarReservaPorFinReserva(listaL);
                }
            }
        }
        
        if (idManzana.equals("0")) {
            setLotes(getPreventaController().fillAllLotes(new Manzana(idManzana)));
        } else if (getValorFiltroSeleccionado().equals("Todos")) {
            setLotes(getPreventaController().fillAllLotes(new Manzana(idManzana)));
        } else if (getValorFiltroSeleccionado().equals("Solo disponibles")) {
            List<Lote> listaLotes = getPreventaController().fillAllLotes(new Manzana(idManzana));                                  
            for (int i = 0; i <= getLotes().size() - 1; i++) {
                if (listaLotes.get(i).getEstatusLote() != EstatusLote.DISPONIBLE) {
                    listaLotes.remove(i);
                    i--;
                }
            }
            setLotes(listaLotes);
        }
    }

    public void updateManzanas(String idPredio) {
        setValidadorBotonImagen(false);
        setPredioConstruido(new Predio());
        setPredioController(new PredioController());
        setPredioConstruido(getPredioController().fillPredioDatatableByid(idPredio));
        if (getPredioConstruido().getCroquis() != null) {
            setValidadorBotonImagen(true);
        }
        if (getValorFiltroSeleccionado().equals("Todos")) {
            Manzana manzana = new Manzana();
            manzana.setId("0");
            manzana.setPredio(new Predio(idPredio));
            manzana.setNombre("Selecciona una manzana");
            setManzanas(getPreventaController().fillAllManzanas(new Predio(idPredio)));
            getManzanas().add(manzana);
            Collections.reverse(getManzanas());
            setLotes(getPreventaController().fillAllLotes(new Manzana("0")));
            setActiveTap("0");
        } else if (getValorFiltroSeleccionado().equals("Solo disponibles")) {
            Manzana manzana = new Manzana();
            manzana.setId("0");
            manzana.setPredio(new Predio(idPredio));
            manzana.setNombre("Selecciona una manzana");
            setManzanas(getPreventaController().fillAllManzanas(new Predio(idPredio)));
            for (int i = 0; i <= getManzanas().size() - 1; i++) {
                long validadorManzana = getPreventaController().countLotesByManzana(getManzanas().get(i).getId());
                if (validadorManzana == 0) {
                    getManzanas().remove(i);
                    i--;
                } else {
                    List<Lote> listaLoteManzana = getPreventaController().fillAllLotes(getManzanas().get(i));
                    for (int j = 0; j <= listaLoteManzana.size() - 1; j++) {
                        if (listaLoteManzana.get(j).getEstatusLote() != EstatusLote.DISPONIBLE) {
                            listaLoteManzana.remove(j);
                            j--;
                        }
                    }
                    if (listaLoteManzana.size() == 0) {
                        getManzanas().remove(i);
                        i--;
                    }
                }
            }
            getManzanas().add(manzana);
            Collections.reverse(getManzanas());
            setLotes(getPreventaController().fillAllLotes(new Manzana("0")));
            setActiveTap("0");
        }

    }

    public void updateTipoActionLote(String tipo) {
        setActive(false);
        setActive2(true);
        setSelectedClient(getSelectedLote().getCliente() == null ? "0" : getSelectedLote().getCliente().getId());
        setPeriodoPago(getSelectedLote().getPeriodoPago() == null ? "null" : getSelectedLote().getPeriodoPago().name());
        setTipoActionLote(tipo);
        if(getSelectedLote().getPeriodoPago().equals(PeriodoPago.QUINCENAL)){
            setActiveInput(true);
            getSelectedLote().setDiaAPagar(new Date());
        }else{
            setActiveInput(false);
        }                
        setAnticipoReserva(0.0);
        if (tipo.equals("1")) {
            setTipoPago(false);
            setTipoAbono(true);
        } else {
            setTipoPago(true);
            setTipoAbono(false);
        }
    }

    public void updateByPaymentPeriod() {
        if (getPeriodoPago().equals("QUINCENAL")) {
            setActiveInput(true);
            getSelectedLote().setDiaAPagar(new Date());
        } else {
            setActiveInput(false);
        }
    }

    public void resetselectedLote() {
        setActive(false);
        setActive2(true);
        setComentario("");
        setSelectedLote(new Lote());
        setSelectedClient("0");
        setPeriodoPago("null");
    }

    public void cancelarReserva() {
        try {
            getSelectedLote().setEstatusLote(EstatusLote.DISPONIBLE);
            getSelectedLote().setLastUpdatedBy(getLoggedUser());
            getSelectedLote().setLastUpdatedOn(new Date());
            // Se cancelan los anticipos por reserva en caso de haberlos
            setCobranzaController(new CobranzaController());
            setAbonoLoteController(new AbonoLoteController());
            setAbonos(getAbonoLoteController().fillAbonoLoteByLoteAndTipoAbono(getSelectedLote(), TipoAbono.ANTICIPO));
            for (AbonoLote abono : getAbonos()) {
                abono.setEstatusActivo(GenericEnumType.DISABLED);
                getCobranzaController().addAbonoLote(abono, getLoggedUser(), false);
            }
            setPredioController(new PredioController());
            getPredioController().addLote(getSelectedLote(), getLoggedUser(), false);
            showMessage(FacesMessage.SEVERITY_INFO, "Cancelado", "Lote cancelado correctamente");
            setLotes(getPreventaController().fillAllLotes(getSelectedLote().getManzana()));
        } catch (Exception e) {
            logger.error("cancelarReserva: " + e);
            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al reservar");
        }
    }
    
     public void cancelarReservaPorFinReserva(Lote lote) {
         logger.error("Cancelando reserva de manera automática por fin de la reserva");
        try {
            lote.setEstatusLote(EstatusLote.DISPONIBLE);
            lote.setLastUpdatedBy(getLoggedUser());
            lote.setLastUpdatedOn(new Date());
            // Se cancelan los anticipos por reserva en caso de haberlos
            setCobranzaController(new CobranzaController());
            setAbonoLoteController(new AbonoLoteController());
            setAbonos(getAbonoLoteController().fillAbonoLoteByLoteAndTipoAbono(lote, TipoAbono.ANTICIPO));
            for (AbonoLote abono : getAbonos()) {
                abono.setEstatusActivo(GenericEnumType.DISABLED);
                getCobranzaController().addAbonoLote(abono, getLoggedUser(), false);
            }
            setPredioController(new PredioController());
            getPredioController().addLote(lote, getLoggedUser(), false);
            showMessage(FacesMessage.SEVERITY_INFO, "Cancelado", "Se cancelaron lotes en reservas por fin de reserva");
        } catch (Exception e) {
            logger.error("cancelarReserva: " + e);
            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al cancelar reserva de lotes por fin de reserva");
        }
    }

    public void solicitarCancelacion() {
        logger.info("Solicita cancelacion de la compra del lote: " + getSelectedLote().getId());
        try {
            // Se valida si existen solitudes de trasferencia pendiente
            setCambiaLoteController(new CambiaLoteController());
            setDevolucionAbonoController(new DevolucionAbonoController());
            setAprobarCancelacionController(new AprobarCancelacionController());
            List<SolicitudTrasferenciaLote> solicitudT = getCambiaLoteController().fillSolicitudesTrasferenciaPendienteByLote(getSelectedLote().getId());
            List<SolicitudDevolucionAbono> solicitudA = getDevolucionAbonoController().fillAllSolicitudDevolucionAbonoByLoteAndEstatus(getSelectedLote(), EstatusSolicitud.PENDIENTE);
            List<SolicitudesCancelacionCompraLote> solicitudC = getAprobarCancelacionController().fillAllSolicitudesByLoteAndEstatus(getSelectedLote(), EstatusSolicitud.PENDIENTE);
            if (!solicitudC.isEmpty()) {
                showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ya ahí una solicitud de cancelación en proceso");
            } else if (solicitudA.isEmpty() && solicitudT.isEmpty()) {
                setAprobarCancelacionController(new AprobarCancelacionController());
                setPredioController(new PredioController());
                //Se genera la solicitud
                SolicitudesCancelacionCompraLote solicitud = new SolicitudesCancelacionCompraLote();
                solicitud.setId(UUID.randomUUID().toString());
                solicitud.setLote(getSelectedLote());
                solicitud.setCliente(getSelectedLote().getCliente());
                solicitud.setEstatusActivo(GenericEnumType.ENABLED);
                solicitud.setEstatusSolicitud(EstatusSolicitud.PENDIENTE);
                solicitud.setComentario(getComentario());
                getAprobarCancelacionController().addSolcitud(solicitud, getLoggedUser(), true);
                // Se verifica si existen pagos realizados asociados al lote a cancelar 
                List<AbonoLote> abonosL = getAprobarCancelacionController().fillAbonoLoteByLoteAndPagoEstatus(getSelectedLote(), GenericEnumType.ENABLED);
                if (!abonosL.isEmpty()) {
                    // Se extrae el total de los pagos realizados
                    Double totalPago = 0.0;
                    for (AbonoLote abono : abonosL) {
                        totalPago = totalPago + abono.getCantidadPagada();
                    }
                    Double descuento = totalPago * (getDescuentoRembolso() / 100);
                    Double totalRembolso = totalPago - descuento;
                    // Se genera un rembolso para la solicitud de cancelación emitida
                    RembolsosCancelacionCompraLote rembolso = new RembolsosCancelacionCompraLote();
                    rembolso.setId(UUID.randomUUID().toString());
                    rembolso.setLote(getSelectedLote());
                    rembolso.setSolicitud(solicitud);
                    rembolso.setEstatusActivo(GenericEnumType.ENABLED);
                    rembolso.setEstatusRembolso(EstatusRembolso.ESPERA);
                    rembolso.setDescuento(getDescuentoRembolso());
                    rembolso.setRembolso(totalRembolso);
                    rembolso.setTotalAbonos(totalPago);
                    getAprobarCancelacionController().addRembolso(rembolso, getLoggedUser(), true);
                }
                
                // Se actualiza el estatus del lote
                getSelectedLote().setEstatusLote(EstatusLote.CANCELADO);
                getSelectedLote().setLastUpdatedBy(getLoggedUser());
                getSelectedLote().setLastUpdatedOn(new Date());
                getPredioController().addLote(getSelectedLote(), getLoggedUser(), false);
                
                logger.info("La solicitud de cancelación se generó correctamente: " + getSelectedLote().getId());
                showMessage(FacesMessage.SEVERITY_INFO, "Solicitado", "La solicitud de cancelación se generó correctamente");
            } else {
                showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ahí solicitudes de trasferencia de lote o de cancelación de abono pendiente para el lote seleccionado, apruébelas o cancélelas para realizar a cancelación");
            }
        } catch (Exception e) {
            logger.error("solicitarcancelacion: " + e);
            showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Error generar la solicitud de cancelación.");
        }
    }

    public Boolean verificarPagoExtra() {
        //GENERACIÓN DE FECHAS DE PAGO
        //Definición de la fecha de inicio y numero de pagos
        Calendar fechaInicial = Calendar.getInstance();
        fechaInicial.setTime(getSelectedLote().getDiaAPagar());
        fechaInicial.add(Calendar.DATE, 1);

        //showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al capturar el abono del dia (" + fechaInicial.getTime() + ")");
        int numeroPagos = Integer.parseInt(getSelectedLote().getPlazoMeses());
        //lista de fechas de pago
        List<Calendar> fechasDePago;

        // pago extra
        Date fechaDate = getFechaPagoExtra();
        Calendar fechaExtra = Calendar.getInstance();
        fechaExtra.setTime(fechaDate);
        fechaExtra.add(Calendar.DATE, 1);
        int mesPagoExtra = fechaExtra.get(Calendar.MONTH);//int mesDeseado = Calendar.OCTOBER; // Mes deseado: octubre
        int diaPagoExtra = fechaExtra.get(Calendar.DAY_OF_MONTH);//int diaDeseado = 15; // Día deseado: 15                
        Boolean valPagoExtra = getPagoExtra();

        // se valida el perido de pago
        Boolean mensual;
        mensual = getSelectedLote().getPeriodoPago() == PeriodoPago.MENSUAL;

        // se genera la lista de fechas
        if (valPagoExtra == true) {
            fechasDePago = generarFechasDePagoExtra(fechaInicial, numeroPagos, mensual, mesPagoExtra, diaPagoExtra);
        } else {
            fechasDePago = generarFechasDePago(fechaInicial, numeroPagos, mensual);
        }

        // Verifico cuantos pagos extra se daran
        int contadorPagosExtra = 0;
        for (Calendar fecha : fechasDePago) {
            int mesFecha = fecha.get(Calendar.MONTH);
            int diaFecha = fecha.get(Calendar.DAY_OF_MONTH);

            if (mesFecha == mesPagoExtra && diaFecha == diaPagoExtra) {
                contadorPagosExtra++;
            }
        }
        // Total a pagar por pagos extra 
        Double totalPagoExtra = getCantidadPagoExtra() * contadorPagosExtra;

        // CALCULOS DE TOTALES 
        Double InteresMensualPago = getSelectedLote().getCosto() * (getSelectedLote().getManzana().getPredio().getInteresMensual() / 100);
        Double Intereses = InteresMensualPago * Integer.parseInt(getSelectedLote().getPlazoMeses());
        Double TotalAPagar = getSelectedLote().getCosto() + Intereses;
        Double TotalCreadito = TotalAPagar + getSelectedLote().getManzana().getPredio().getComisionMensual();
        //se extrae los anticipos previos en caso de haber dado alguno durante la reserva 
        setAbonoLoteController(new AbonoLoteController());
        List<AbonoLote> anticiposR = getAbonoLoteController().fillAbonoLoteByLoteAndTipoAbono(getSelectedLote(), TipoAbono.ANTICIPO);
        Double totalAnticipoR = 0.0;
        if (anticiposR != null) {
            for (AbonoLote anticipo : anticiposR) {
                totalAnticipoR = totalAnticipoR + anticipo.getPago();
            }
        }
        // se calcula el anticipo total
        Double totalAnticipo = getSelectedLote().getAnticipo() + totalAnticipoR;
        Double TotalCreaditoA = TotalCreadito - totalAnticipo;

        Double diferenciaPagos = TotalCreaditoA - totalPagoExtra;
        Boolean valTotal;
        valTotal = diferenciaPagos >= 0;
        return valTotal;
    }

    public void updateLote() {
        setActive(true);
        logger.info("updateLote");                        
        try {
            
            setGenericController(new GenericValidationController());
            if (getGenericController().existClosingDayByCreatedOn(new Date(), getLoggedUser())) {
                showMessage(FacesMessage.SEVERITY_WARN, "Error","No se puede hacer ninguna acción si existe un corte del día");                
                return;
            }                  
            if (getGenericController().existStableGeneralBoxByCurdate(getLoggedUser())) {
                showMessage(FacesMessage.SEVERITY_WARN,"Dia cerrado","No se pueden eliminar más registros porque ya existe un cuadre de caja chica del día");                
                return;
            }
            
            if (getSelectedLote().getCosto() != null && getSelectedLote().getCosto() > 0) {
                setPredioController(new PredioController());
                getSelectedLote().setCliente(new Person(getSelectedClient()));
                getSelectedLote().setLastUpdatedBy(getLoggedUser());
                getSelectedLote().setLastUpdatedOn(new Date());

                if ("2".equals(getTipoActionLote())) {

                    getSelectedLote().setEstatusLote(EstatusLote.VENDIDO);
                    if (getPeriodoPago().equals("MENSUAL")) {
                        getSelectedLote().setPeriodoPago(PeriodoPago.MENSUAL);
                    } else if (getPeriodoPago().equals("QUINCENAL")) {
                        getSelectedLote().setPeriodoPago(PeriodoPago.QUINCENAL);
                    }

                    if (getSelectedLote().getAnticipo() <= getSelectedLote().getCosto()) {
                        // se valida si se introdujo un pago extra
                        setPagoExtra(false);
                        if (getFechaPagoExtra() != null && getCantidadPagoExtra() == null) {
                            setActive(false);
                            showMessage(FacesMessage.SEVERITY_ERROR, "Advertencia", "Defina un monto para el pago extra");
                        } else if (getCantidadPagoExtra() != null && getFechaPagoExtra() == null) {
                            setActive(false);
                            showMessage(FacesMessage.SEVERITY_ERROR, "Advertencia", "Defina una fecha para el pago extra");
                        } else if (getFechaPagoExtra() != null && getCantidadPagoExtra() != null) {
                            setPagoExtra(true);
                            // Convierto la fecha del pago extra a calendar para verificar el dia seleccionado
                            Date fechaEx = getFechaPagoExtra();
                            Calendar fechaExtra = Calendar.getInstance();
                            fechaExtra.setTime(fechaEx);
                            int diaPagoExtra = fechaExtra.get(Calendar.DAY_OF_MONTH);
                            //Convierto la fecha en el que comenzaran  los abonos para verificar el dia seleccionado
                            Date fechaIni = getSelectedLote().getDiaAPagar();
                            Calendar fechaInicio = Calendar.getInstance();
                            fechaInicio.setTime(fechaIni);
                            int diaFechaInicio = fechaInicio.get(Calendar.DAY_OF_MONTH);
                            // Se valida que el numero del dia seleccionado no sea el mismo que el del pago extra
                            System.out.println(diaFechaInicio + ' ' + diaPagoExtra);
                            if (diaPagoExtra == diaFechaInicio) {
                                setActive(false);
                                showMessage(FacesMessage.SEVERITY_ERROR, "Advertencia", "El número del día del pago extra no puede ser el mimo que el de los abonos");
                            } else if (verificarPagoExtra()) {
                                getPredioController().addLote(getSelectedLote(), getLoggedUser(), false);
                                crearCorrida(getSelectedLote());
                                setActive(true);
                                setActive2(false);
                                showMessage(FacesMessage.SEVERITY_INFO, "Comprado", "Lote comprado correctamente");
                            } else {
                                setActive(false);
                                showMessage(FacesMessage.SEVERITY_ERROR, "Advertencia", "El pago extra es demasiado grande Seleccione un monto más pequeño");
                            }
                        } else {
                            getPredioController().addLote(getSelectedLote(), getLoggedUser(), false);
                            crearCorrida(getSelectedLote());
                            setActive(true);
                            setActive2(false);
                            showMessage(FacesMessage.SEVERITY_INFO, "Comprado", "Lote comprado correctamente");
                        }

                    } else {
                        setActive(false);
                        showMessage(FacesMessage.SEVERITY_ERROR, "Error", "El anticipo no puede ser mayor al precio del lote.");
                    }

                } else {
                    if (!getAnticipoReserva().isNaN() && getAnticipoReserva() > 0.0) {
                        try {
                            Date fechaActual = new Date();
                            Calendar calendar = Calendar.getInstance();
                            calendar.setTime(fechaActual);
                            calendar.add(Calendar.DATE, 1);
                            Date fechaNueva = calendar.getTime();
                            setAbonoLoteController(new AbonoLoteController());
                            AbonoLote anticipo = new AbonoLote();
                            anticipo.setLote(getSelectedLote());
                            anticipo.setCliente(getSelectedLote().getCliente());
                            anticipo.setTipoAbono(TipoAbono.ANTICIPO);
                            anticipo.setEstatusActivo(GenericEnumType.ENABLED);
                            anticipo.setPagoEstatus(GenericEnumType.ENABLED);
                            anticipo.setFechaAPagar(fechaNueva);
                            anticipo.setPago(getAnticipoReserva());
                            anticipo.setSaldoAnterior(0.0);
                            anticipo.setSaldoNuevo(0.0);
                            anticipo.setCreatedBy(getLoggedUser());
                            anticipo.setCreatedOn(fechaNueva);
                            anticipo.setLastUpdatedBy(getLoggedUser());
                            anticipo.setLastUpdatedOn(fechaNueva);
                            anticipo.setCantidadPagada(getAnticipoReserva());
                            anticipo.setMandadoPagado(0.0);
                            anticipo.setMandadoPagar(0.0);
                            anticipo.setCobranzaPagada(0.0);
                            anticipo.setCobranzaPagar(0.0);
                            getAbonoLoteController().addAbonoLote(anticipo, getLoggedUser());
                        } catch (Exception e) {
                            logger.error("Error al capturar anticipo por reserva  : " + e);
                            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al capturar el anticipo por reserva ");
                        }
                    }
                    getSelectedLote().setEstatusLote(EstatusLote.APARTADO);
                    getPredioController().addLote(getSelectedLote(), getLoggedUser(), false);
                    showMessage(FacesMessage.SEVERITY_INFO, "Reservado", "Lote reservado correctamente");
                }

                setLotes(getPreventaController().fillAllLotes(getSelectedLote().getManzana()));
            } else {
                showMessage(FacesMessage.SEVERITY_WARN, "Error", "Asigne un costo mayor a 0 para comprar el lote ");
            }
        } catch (IOException e) {
            logger.error("updateLote: " + e);
            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al reservar" + e);
        }
    }

    public void crearCorrida(Lote lote) throws IOException {
        logger.info("Creación de abonos");

        //GENERACIÓN DE FECHAS DE PAGO
        //Definición de la fecha de inicio mas un dia y numero de pagos
        Calendar fechaInicial = Calendar.getInstance();
        fechaInicial.setTime(lote.getDiaAPagar());
        fechaInicial.add(Calendar.DATE, 1);

        int numeroPagos = Integer.parseInt(lote.getPlazoMeses());
        //lista de fechas de pago
        List<Calendar> fechasDePago = new ArrayList<>();

        // pago extra                   
        Boolean valPagoExtra = getPagoExtra();
        Double montoPagoExtra = 0.0;
        int contadorPagosExtra = 0;
        Double totalPagoExtra = 0.0;
        int mesPagoExtra = 0;
        int diaPagoExtra = 0;

        // se valida el perido de pago
        Boolean mensual;
        mensual = lote.getPeriodoPago() == PeriodoPago.MENSUAL;

        // se genera la lista de fechas
        if (valPagoExtra == true) {
            // se asigna el pago extra
            montoPagoExtra = getCantidadPagoExtra();
            // se extrae el dia y mes del pago extra
            Date fechaDateEX = getFechaPagoExtra();
            Calendar fechaExtra = Calendar.getInstance();
            fechaExtra.setTime(fechaDateEX);
            fechaExtra.add(Calendar.DATE, 1);
            mesPagoExtra = fechaExtra.get(Calendar.MONTH);
            diaPagoExtra = fechaExtra.get(Calendar.DAY_OF_MONTH);
            fechasDePago = generarFechasDePagoExtra(fechaInicial, numeroPagos, mensual, mesPagoExtra, diaPagoExtra);
            // Verifico cuantos pagos extra se daran       
            for (Calendar fecha : fechasDePago) {
                int mesFecha = fecha.get(Calendar.MONTH);
                int diaFecha = fecha.get(Calendar.DAY_OF_MONTH);
                if (mesFecha == mesPagoExtra && diaFecha == diaPagoExtra) {
                    contadorPagosExtra++;
                }
            }
        } else {
            fechasDePago = generarFechasDePago(fechaInicial, numeroPagos, mensual);
        }

        // Total a pagar por pagos extra 
        totalPagoExtra = montoPagoExtra * contadorPagosExtra;
        // CALCULOS DE TOTALES 
        Double InteresMensualPago = lote.getCosto() * (lote.getManzana().getPredio().getInteresMensual() / 100);
        Double Intereses = InteresMensualPago * Integer.parseInt(lote.getPlazoMeses());
        Double TotalAPagar = lote.getCosto() + Intereses;
        Double TotalCreadito = TotalAPagar + lote.getManzana().getPredio().getComisionMensual();
        //se extrae los anticipos previos en caso de haber dado alguno durante la reserva 
        setAbonoLoteController(new AbonoLoteController());
        List<AbonoLote> anticiposR = getAbonoLoteController().fillAbonoLoteByLoteAndTipoAbono(lote, TipoAbono.ANTICIPO);
        Double totalAnticipoR = 0.0;
        if (anticiposR != null) {
            for (AbonoLote anticipo : anticiposR) {
                totalAnticipoR = totalAnticipoR + anticipo.getPago();
            }
        }
        // se calcula el anticipo total
        Double totalAnticipo = lote.getAnticipo() + totalAnticipoR;
        Double TotalCreaditoA = TotalCreadito - totalAnticipo;
        Double pagoPorMes = (TotalCreaditoA - totalPagoExtra) / Integer.parseInt(lote.getPlazoMeses());

        //ACTUALIZACIÓN DEL LOS DATOS DEL LOTE 
        try {
            lote.setAnticipo(totalAnticipo);
            lote.setTotalAPagar(TotalCreadito);
            lote.setTotalPago(TotalCreaditoA);
            lote.setMensualidad(pagoPorMes);
            if (lote.getPeriodoPago().equals(PeriodoPago.QUINCENAL)) {
                Calendar fechaActual = (Calendar) Calendar.getInstance();
                if (fechaActual.get(Calendar.DAY_OF_MONTH) != 15) {
                    if (fechaActual.get(Calendar.DAY_OF_MONTH) < 15) {
                        fechaActual.set(Calendar.DAY_OF_MONTH, 15);
                    } else {
                        fechaActual.add(Calendar.MONTH, 1);
                        fechaActual.set(Calendar.DAY_OF_MONTH, 15);
                    }
                } else {
                    fechaActual.add(Calendar.MONTH, 1);
                    fechaActual.set(Calendar.DAY_OF_MONTH, fechaActual.getActualMaximum(Calendar.DAY_OF_MONTH));
                }
                lote.setDiaAPagar(fechaActual.getTime());
            }
            lote.setLastUpdatedBy(getLoggedUser());
            lote.setLastUpdatedOn(new Date());
            setPredioController(new PredioController());
            getPredioController().addLote(lote, getLoggedUser(), false);
        } catch (Exception e) {
            logger.error("Error al actualizar  los datos del pago en lote: " + e);
            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al actualizar  los datos del pago en lote");
        }

        //SE CREA UN PRIMER ABONO CON EL TOTAL DE LOS ANTICIPOS SI LOS HUBO 
        if (lote.getAnticipo() > 0) {
            try {
                Date fechaActual = new Date();
                Calendar calendar2 = Calendar.getInstance();
                calendar2.setTime(fechaActual);
                calendar2.add(Calendar.DATE, 1);
                Date fechaNueva = calendar2.getTime();
                AbonoLote anticipo = new AbonoLote();
                anticipo.setLote(lote);
                anticipo.setCliente(lote.getCliente());
                anticipo.setTipoAbono(TipoAbono.ANTICIPO);
                anticipo.setEstatusActivo(GenericEnumType.ENABLED);
                anticipo.setPagoEstatus(GenericEnumType.ENABLED);
                anticipo.setFechaAPagar(fechaNueva);
                anticipo.setPago(lote.getAnticipo());
                anticipo.setSaldoAnterior(TotalCreadito);
                anticipo.setSaldoNuevo(TotalCreaditoA);
                anticipo.setCreatedBy(getLoggedUser());
                anticipo.setCreatedOn(fechaNueva);
                anticipo.setLastUpdatedBy(getLoggedUser());
                anticipo.setLastUpdatedOn(fechaNueva);
                anticipo.setCantidadPagada(lote.getAnticipo() - totalAnticipoR < 0 ? 0.0 : lote.getAnticipo() - totalAnticipoR);
                anticipo.setMandadoPagado(0.0);
                anticipo.setMandadoPagar(0.0);
                anticipo.setCobranzaPagada(0.0);
                anticipo.setCobranzaPagar(0.0);
                getAbonoLoteController().addAbonoLote(anticipo, getLoggedUser());
            } catch (Exception e) {
                logger.error("Error al capturar el primer abono del tipo anticipo : " + e);
                showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al capturar el anticipo ");
            }
        }

        // CREACIÓN DE PAGOS POR FECHA
        setActualizarPago(TotalCreaditoA);
        recorrerFechas(fechasDePago, lote, pagoPorMes, mesPagoExtra, diaPagoExtra, montoPagoExtra);

        URL url;

        if (getSelectedLote().getManzana().getPredio().getLocation().getLogo() != null) {
            url = new URL("https://crov-inmobiliaria.s3.us-east-2.amazonaws.com/" + getSelectedLote().getManzana().getPredio().getLocation().getLogo());

        } else {
            url = new URL("https://crov-inmobiliaria.s3.us-east-2.amazonaws.com/crov-inmobiliaria-logo.png");
        }

        java.awt.Image imagenEmpresa = ImageIO.read(url);
        setCobranzaController(new CobranzaController());
        setAbonos(getCobranzaController().fillAbonoLoteByLote(getSelectedLote()));
        Map<String, Object> parameters = new HashMap<String, Object>();
        JRBeanCollectionDataSource abonosLote = new JRBeanCollectionDataSource(getAbonos());
        parameters.put("abonosLote", abonosLote);
        parameters.put("imagenEmpresa", imagenEmpresa);
        parameters.put("nombreEmpresa", getLoggedUser().getLocation().getBusinessName() != null ? getLoggedUser().getLocation().getBusinessName() : "");
        parameters.put("rfcEmpresa", getLoggedUser().getLocation().getRfc() != null ? getLoggedUser().getLocation().getRfc() : "");
        parameters.put("correoEmpresa", getLoggedUser().getLocation().getMail() != null ? getLoggedUser().getLocation().getMail() : "");
        parameters.put("telefonoEmpresa", getLoggedUser().getLocation().getPhone() != null ? getLoggedUser().getLocation().getPhone() : "");
        parameters.put("regimenEmpresa", getLoggedUser().getLocation().getTaxRegime() != null ? getLoggedUser().getLocation().getTaxRegime().getNombre() : "");
        parameters.put("direccionEmpresa", getLoggedUser().getLocation().getAddress() != null ? getLoggedUser().getLocation().getAddress() : "");
        //datos de persona
        setCustomerController(new CustomerController());
        Person persona = getCustomerController().getPersonById(lote.getCliente().getId());
        parameters.put("nombreCliente", persona.getFirstName() + " " + persona.getSecondName() + " " + persona.getLastName() + " " + persona.getMiddleName());
        ContactNumber contactNumber = getAbonoLoteController().finPhoneByClient(lote.getCliente().getId());
        parameters.put("contactNumber", contactNumber != null ? contactNumber.getPhone() : "");

        parameters.put("folioLote", lote.getFolio() != null ? lote.getFolio() : "");
        parameters.put("fecha", new Date());
        parameters.put("cantidadPagos", lote.getPlazoMeses() != null ? lote.getPlazoMeses() : "");
        parameters.put("periodoPago", lote.getPeriodoPago() != null ? lote.getPeriodoPago().name() : "");
        parameters.put("primerPago", lote.getDiaAPagar() != null ? lote.getDiaAPagar() : "");
        parameters.put("interes", lote.getManzana().getPredio().getInteresMensual() != null ? lote.getManzana().getPredio().getInteresMensual() : "");
        parameters.put("interesMensual", InteresMensualPago);
        parameters.put("interesTotal", Intereses);
        parameters.put("costoInicial", lote.getCosto() != null ? lote.getCosto() : "");
        parameters.put("anticipo", lote.getAnticipo() != null ? lote.getAnticipo() : "");
        parameters.put("comisionApertura", lote.getManzana().getPredio().getComisionMensual() != null ? lote.getManzana().getPredio().getComisionMensual() : "");
        parameters.put("totalPagar", TotalCreaditoA);
        try {
            String filePath = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/resources/jasper-reports/CompraLote.jrxml");
            File file = new File(filePath);
            JasperDesign jasperDesign = JRXmlLoader.load(file);
            JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
            JasperPrint printt = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
            //Se castea a pdf
            JasperExportManager.exportReportToPdfFile(printt, "Lote.pdf");
            setPrint(printt);
        } catch (JRException e) {
            logger.error("Error al generar el reporte de compra : " + e);
            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al generar el reporte de compra.");
        }

    }

    public void recorrerFechas(List<Calendar> fechas, Lote lote, Double pagoPorMes, int mesPagoExtra, int diaPagoExtra, Double montoPagoExtra) {
        logger.info("Insertando abonos por fecha");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");

        // Usamos Collections.sort() para ordenar la lista de fechas
        Collections.sort(fechas, new Comparator<Calendar>() {
            @Override
            public int compare(Calendar fecha1, Calendar fecha2) {
                // Comparamos las fechas usando el método compareTo()
                return fecha1.compareTo(fecha2);
            }
        });

        // recorre las fechas ordenadas
        for (Calendar fecha : fechas) {

            try {
                setAbonoLoteController(new AbonoLoteController());
                AbonoLote abono = new AbonoLote();
                abono.setLote(lote);
                abono.setCliente(lote.getCliente());
                abono.setTipoAbono(TipoAbono.ABONO);
                abono.setEstatusActivo(GenericEnumType.ENABLED);
                abono.setPagoEstatus(GenericEnumType.DISABLED);
                abono.setFechaAPagar(fecha.getTime());
                abono.setSaldoAnterior(getActualizarPago());
                if (fecha.get(Calendar.MONTH) == mesPagoExtra && fecha.get(Calendar.DAY_OF_MONTH) == diaPagoExtra) {
                    abono.setPago(montoPagoExtra);
                    setActualizarPago(getActualizarPago() - montoPagoExtra);
                    abono.setSaldoNuevo(getActualizarPago());
                } else {
                    abono.setPago(pagoPorMes);
                    setActualizarPago(getActualizarPago() - pagoPorMes);
                    abono.setSaldoNuevo(getActualizarPago());
                }
                abono.setCreatedBy(getLoggedUser());
                abono.setCreatedOn(new Date());
                getAbonoLoteController().addAbonoLote(abono, getLoggedUser());
            } catch (Exception e) {
                logger.error("Error al capturar el abono del dia: (" + fecha + ") " + e);
                showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al capturar el abono del dia (" + fecha + ")");
            }

        }
    }

    public List<Calendar> generarFechasDePagoExtra(Calendar fechaInicial, int numeroPagos, Boolean mensual, int mesPagoExtra, int diaPagoExtra) {
        logger.info("Generación fechas con pago extra");
        //lista de fechas
        List<Calendar> fechasDePago = new ArrayList<>();
        if (mensual) {
            fechasDePago.add(fechaInicial);
        }
        Calendar fechaActual = (Calendar) fechaInicial.clone();
        //captura el dia de una fecha en caso de que no se encuntre en le mes siguinte 
        boolean detenerFecha = false;
        Calendar fechaDetenida = Calendar.getInstance();
        // almacena el estatus del pago extra
        boolean fechaDeseadaAgregada = false;

        int pagosGenerados = 0;
        // se verifica el tipo de pago
        if (mensual) {
            for (int i = 1; i < numeroPagos; i++) {
                // Verifica si el mes actual coincide con el mes deseado
                if (fechaActual.get(Calendar.MONTH) == mesPagoExtra) {
                    // Agrega un registro extra con la fecha específica
                    Calendar fechaExtra = Calendar.getInstance();
                    fechaExtra.set(fechaActual.get(Calendar.YEAR), mesPagoExtra, diaPagoExtra);
                    fechasDePago.add(fechaExtra);
                    fechaDeseadaAgregada = true;
                }

                Calendar fechaSiguiente = (Calendar) fechaActual.clone();
                fechaSiguiente.add(Calendar.MONTH, 1);
                if (detenerFecha == false) {
                    if (fechaActual.get(Calendar.DAY_OF_MONTH) > fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH)) {
                        detenerFecha = true;
                        fechaDetenida = (Calendar) fechaActual.clone();
                        fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
                    }
                } else {
                    if (fechaSiguiente.get(Calendar.DAY_OF_MONTH) <= fechaDetenida.get(Calendar.DAY_OF_MONTH)) {
                        detenerFecha = false;
                        fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaDetenida.get(Calendar.DAY_OF_MONTH));
                    } else {
                        fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
                    }
                }
                fechasDePago.add(fechaSiguiente);
                fechaActual = fechaSiguiente;
                pagosGenerados++;
            }

        } else {
            for (int i = 0; i < numeroPagos; ) {
                Calendar fechaSiguiente = (Calendar) fechaActual.clone();
                fechaSiguiente.set(Calendar.DAY_OF_MONTH, 15);
                fechasDePago.add(fechaSiguiente);
                i++;

                if (i < numeroPagos) {
                    fechaSiguiente = (Calendar) fechaActual.clone();
                    fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
                    fechasDePago.add(fechaSiguiente);
                    i++;
                }

                if (!fechaDeseadaAgregada) {
                    // Agrega la fecha extra con la fecha específica
                    Calendar fechaExtra = Calendar.getInstance();
                    fechaExtra.set(fechaActual.get(Calendar.YEAR), mesPagoExtra, diaPagoExtra);
                    fechasDePago.add(fechaExtra);
                    fechaDeseadaAgregada = true;
                }

                fechaActual.add(Calendar.MONTH, 1);
            }

            // Ordenar la lista de fechas de pago
            Collections.sort(fechasDePago);
        }

        // Si el número de pagos generado es menor que el número de pagos deseado y aún no se ha agregado la fecha deseada, la agregamos al final
        if (pagosGenerados <= numeroPagos && !fechaDeseadaAgregada) {
            Calendar fechaExtra = Calendar.getInstance();
            fechaExtra.set(fechaActual.get(Calendar.YEAR), mesPagoExtra, diaPagoExtra);
            fechasDePago.add(fechaExtra);
        }

        return fechasDePago;
    }

    public List<Calendar> generarFechasDePago(Calendar fechaInicial, int numeroPagos, Boolean mensual) {
        logger.info("Generación fechas");
        //lista de fechas
        List<Calendar> fechasDePago = new ArrayList<>();
        if (mensual) {
            fechasDePago.add(fechaInicial);
        }
        Calendar fechaActual = (Calendar) fechaInicial.clone();
        //captura el dia de una fecha en caso de que no se encuntre en le mes siguinte 
        boolean detenerFecha = false;
        Calendar fechaDetenida = Calendar.getInstance();

        if (mensual) {
            for (int i = 1; i < numeroPagos; i++) {
                Calendar fechaSiguiente = (Calendar) fechaActual.clone();
                fechaSiguiente.add(Calendar.MONTH, 1);
                if (detenerFecha == false) {
                    if (fechaActual.get(Calendar.DAY_OF_MONTH) > fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH)) {
                        detenerFecha = true;
                        fechaDetenida = (Calendar) fechaActual.clone();
                        fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
                    }
                } else {
                    if (fechaSiguiente.get(Calendar.DAY_OF_MONTH) <= fechaDetenida.get(Calendar.DAY_OF_MONTH)) {
                        detenerFecha = false;
                        fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaDetenida.get(Calendar.DAY_OF_MONTH));
                    } else {
                        fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
                    }
                }
                fechasDePago.add(fechaSiguiente);
                fechaActual = fechaSiguiente;
            }
        } else {
             for (int i = 0; i < numeroPagos; ) {
                Calendar fechaSiguiente = (Calendar) fechaActual.clone();
                fechaSiguiente.set(Calendar.DAY_OF_MONTH, 15);
                fechasDePago.add(fechaSiguiente);
                i++;

                if (i < numeroPagos) {
                    fechaSiguiente = (Calendar) fechaActual.clone();
                    fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
                    fechasDePago.add(fechaSiguiente);
                    i++;
                }
                fechaActual.add(Calendar.MONTH, 1);
            }
        }

        return fechasDePago;
    }

    public void descargarArchivo() throws IOException {
        try {
            HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
            response.addHeader("content-disposition", "attachment;filename = archivo.pdf");
            ServletOutputStream stream = response.getOutputStream();
            JasperExportManager.exportReportToPdfStream(getPrint(), stream);
            FacesContext.getCurrentInstance().responseComplete();
        } catch (IOException | JRException e) {
            logger.error("Error al descargar el reporte de compra : " + e);
        }
    }

    public void generarReposteAbonos() throws IOException {
        //Crear reporte PDF
        //String filePathImagenEmpresa = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/resources/jasper-reports/crov-inmobiliaria-logo.jpg");
        URL url;

        if (getSelectedLote().getManzana().getPredio().getLocation().getLogo() != null) {
            url = new URL("https://crov-inmobiliaria.s3.us-east-2.amazonaws.com/" + getSelectedLote().getManzana().getPredio().getLocation().getLogo());

        } else {
            url = new URL("https://crov-inmobiliaria.s3.us-east-2.amazonaws.com/crov-inmobiliaria-logo.png");
        }

        java.awt.Image imagenEmpresa = ImageIO.read(url);
        setCobranzaController(new CobranzaController());
        Double costo = getSelectedLote().getTotalAPagar();
        setAbonos(getCobranzaController().fillAbonoLoteByLoteAndTipo(getSelectedLote(), TipoAbono.ABONO));      
        List<AbonoLoteView> abonosAndSub = getCobranzaController().fillAbonoLoteAndSubAbonoByLoteAndTipo(getSelectedLote(), TipoAbono.ABONO);        
        
        // Corrección del saldo nuevo y el saldo anterior con base en la cantidad pagada 
        int i=0;
        for (AbonoLoteView abono : abonosAndSub) {            
            if(abono.getTipoAbono().equals(TipoAbono.ABONO)){
                i++;
                abono.setSaldoAnterior(costo);
                abono.setNumeroAbono(String.valueOf(i));
               
                // Validación sub abonos para que solo sume la cantidad pagado del abono lote principal
                if(!abono.getTipoAbono().equals(TipoAbono.SUBABONO)){
                  // Validación de la cantidad por valores nulos 
                  try {
                        costo = costo - abono.getCantidadPagada();
                    } catch (Exception e) {
                        costo = costo - 0;
                    }  
                }
                
                abono.setSaldoNuevo(costo);
            }else{
                abono.setNumeroAbono(String.valueOf(i));
            }            
        }
        Map<String, Object> parameters = new HashMap<String, Object>();
        JRBeanCollectionDataSource abonosLote = new JRBeanCollectionDataSource(abonosAndSub);
        parameters.put("abonosLote", abonosLote);
        parameters.put("imagenEmpresa", imagenEmpresa);
        parameters.put("nombreEmpresa", getLoggedUser().getLocation().getBusinessName() != null ? getLoggedUser().getLocation().getBusinessName() : "");
        parameters.put("rfcEmpresa", getLoggedUser().getLocation().getRfc() != null ? getLoggedUser().getLocation().getRfc() : "");
        parameters.put("correoEmpresa", getLoggedUser().getLocation().getMail() != null ? getLoggedUser().getLocation().getMail() : "");
        parameters.put("telefonoEmpresa", getLoggedUser().getLocation().getPhone() != null ? getLoggedUser().getLocation().getPhone() : "");
        parameters.put("regimenEmpresa", getLoggedUser().getLocation().getTaxRegime() != null ? getLoggedUser().getLocation().getTaxRegime().getNombre() : "");
        parameters.put("direccionEmpresa", getLoggedUser().getLocation().getAddress() != null ? getLoggedUser().getLocation().getAddress() : "");
        //datos de persona
        setCustomerController(new CustomerController());
        Person persona = getCustomerController().getPersonById(getSelectedLote().getCliente().getId());
        parameters.put("nombreCliente", persona.getFirstName() + " " + persona.getSecondName() + " " + persona.getLastName() + " " + persona.getMiddleName());
        setAbonoLoteController(new AbonoLoteController());
        ContactNumber contactNumber = getAbonoLoteController().finPhoneByClient(persona.getId());
        parameters.put("contactNumber", contactNumber != null ? contactNumber.getPhone() : "");

        //Datos predio, manzana, lote
        parameters.put("fecha", new Date());
        parameters.put("folioLote", getSelectedLote().getFolio() != null ? getSelectedLote().getFolio() : "");
        parameters.put("periodoPago", getSelectedLote().getPeriodoPago().name());
        parameters.put("nombreProyecto", getSelectedLote().getManzana().getPredio().getNombreProyecto());
        parameters.put("nombreManzana", getSelectedLote().getManzana().getNombre());
        parameters.put("plazos", getSelectedLote().getPlazoMeses());
        parameters.put("totalPago", getSelectedLote().getTotalPago());
        parameters.put("anticipo", getSelectedLote().getAnticipo());

        //se extraen la cantidad de pagos
        setCobranzaController(new CobranzaController());
        long pagosRestantes = getCobranzaController().countPagosByLote(getSelectedLote().getId());
        parameters.put("adeudo", Math.toIntExact(pagosRestantes));

        //se calcula el total de los pagos realizados    
        Double cantidadPagada = 0.0;
        for (AbonoLote abono : getAbonos()) {
            if (abono.getPagoEstatus().equals(GenericEnumType.ENABLED) && abono.getTipoAbono().equals(TipoAbono.ABONO)) {
                cantidadPagada = cantidadPagada + abono.getCantidadPagada();
            }else if(abono.getPagoEstatus().equals(GenericEnumType.INCOMPLETE)){
                cantidadPagada = cantidadPagada + abono.getCantidadPagada();
            }
        }

        Double adeudo = getSelectedLote().getTotalPago() - cantidadPagada;
        parameters.put("restoPago", adeudo);

        try {
            String filePath = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/resources/jasper-reports/reportePagosLote.jrxml");
            File file = new File(filePath);
            JasperDesign jasperDesign = JRXmlLoader.load(file);
            JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
            JasperPrint printt = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
            JasperExportManager.exportReportToPdfFile(printt, "pagos.pdf");
            setPrint(printt);
        } catch (JRException e) {
            logger.error("Error al generar el reporte de compra : " + e);
            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al generar el reporte de compra.");
        }
    }

    public void advertenciaCostoCero() {
        showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "Asigne un costo mayor a 0 para comprar el lote");
    }

    public void resetExtras() {
        setFechaPagoExtra(null);
        setCantidadPagoExtra(null);
    }

    public PreventaController getPreventaController() {
        return preventaController;
    }

    public void setPreventaController(PreventaController preventaController) {
        this.preventaController = preventaController;
    }

    public List<Predio> getPredios() {
        return predios;
    }

    public void setPredios(List<Predio> predios) {
        this.predios = predios;
    }

    public List<Manzana> getManzanas() {
        return manzanas;
    }

    public void setManzanas(List<Manzana> manzanas) {
        this.manzanas = manzanas;
    }

    public List<Lote> getLotes() {
        return lotes;
    }

    public void setLotes(List<Lote> lotes) {
        this.lotes = lotes;
    }

    public Lote getSelectedLote() {
        return selectedLote;
    }

    public void setSelectedLote(Lote selectedLote) {
        this.selectedLote = selectedLote;
    }

    public PredioController getPredioController() {
        return predioController;
    }

    public void setPredioController(PredioController predioController) {
        this.predioController = predioController;
    }

    public String getPeriodoPago() {
        return periodoPago;
    }

    public void setPeriodoPago(String periodoPago) {
        this.periodoPago = periodoPago;
    }

    public List<PersonListView> getListClient() {
        return listClient;
    }

    public void setListClient(List<PersonListView> listClient) {
        this.listClient = listClient;
    }

    public String getSelectedClient() {
        return selectedClient;
    }

    public void setSelectedClient(String selectedClient) {
        this.selectedClient = selectedClient;
    }

    public String getTipoActionLote() {
        return tipoActionLote;
    }

    public void setTipoActionLote(String tipoActionLote) {
        this.tipoActionLote = tipoActionLote;
    }

    public String getActiveTap() {
        return activeTap;
    }

    public void setActiveTap(String activeTap) {
        this.activeTap = activeTap;
    }

    public AbonoLoteController getAbonoLoteController() {
        return abonoLoteController;
    }

    public void setAbonoLoteController(AbonoLoteController abonoLoteController) {
        this.abonoLoteController = abonoLoteController;
    }

    public Boolean getActiveInput() {
        return activeInput;
    }

    public void setActiveInput(Boolean activeInput) {
        this.activeInput = activeInput;
    }

    public double getActualizarPago() {
        return actualizarPago;
    }

    public void setActualizarPago(double actualizarPago) {
        this.actualizarPago = actualizarPago;
    }

    public Boolean getTipoPago() {
        return tipoPago;
    }

    public void setTipoPago(Boolean tipoPago) {
        this.tipoPago = tipoPago;
    }

    public Boolean getTipoAbono() {
        return tipoAbono;
    }

    public void setTipoAbono(Boolean tipoAbono) {
        this.tipoAbono = tipoAbono;
    }

    public CobranzaController getCobranzaController() {
        return cobranzaController;
    }

    public void setCobranzaController(CobranzaController cobranzaController) {
        this.cobranzaController = cobranzaController;
    }

    public List<AbonoLote> getAbonos() {
        return abonos;
    }

    public void setAbonos(List<AbonoLote> abonos) {
        this.abonos = abonos;
    }

    public CustomerController getCustomerController() {
        return customerController;
    }

    public void setCustomerController(CustomerController customerController) {
        this.customerController = customerController;
    }

    public JasperPrint getPrint() {
        return print;
    }

    public void setPrint(JasperPrint print) {
        this.print = print;
    }

    public Boolean getActive() {
        return active;
    }

    public void setActive(Boolean active) {
        this.active = active;
    }

    public Boolean getActive2() {
        return active2;
    }

    public void setActive2(Boolean active2) {
        this.active2 = active2;
    }

    public List<String> getValoresFiltro() {
        return valoresFiltro;
    }

    public void setValoresFiltro(List<String> valoresFiltro) {
        this.valoresFiltro = valoresFiltro;
    }

    public String getValorFiltroSeleccionado() {
        return valorFiltroSeleccionado;
    }

    public void setValorFiltroSeleccionado(String valorFiltroSeleccionado) {
        this.valorFiltroSeleccionado = valorFiltroSeleccionado;
    }

    public Date getDateToday() {

        return new Date();
    }

    public String getComentario() {
        return comentario;
    }

    public Person getCustomer() {
        return customer;
    }

    public String getComboPreventaActive() {
        return comboPreventaActive;
    }

    public void setComboPreventaActive(String comboPreventaActive) {
        this.comboPreventaActive = comboPreventaActive;
    }

    public void setCustomer(Person customer) {
        this.customer = customer;
    }

    public void setComentario(String comentario) {
        this.comentario = comentario;
    }

    public CustomerController getControladorCustomer() {
        return controladorCustomer;
    }

    public void setControladorCustomer(CustomerController controladorCustomer) {
        this.controladorCustomer = controladorCustomer;
    }

    public Double getAnticipoReserva() {
        return AnticipoReserva;
    }

    public void setAnticipoReserva(Double AnticipoReserva) {
        this.AnticipoReserva = AnticipoReserva;
    }

    public AprobarCancelacionController getAprobarCancelacionController() {
        return aprobarCancelacionController;
    }

    public void setAprobarCancelacionController(AprobarCancelacionController aprobarCancelacionController) {
        this.aprobarCancelacionController = aprobarCancelacionController;
    }

    public Double getDescuentoRembolso() {
        return descuentoRembolso;
    }

    public void setDescuentoRembolso(Double descuentoRembolso) {
        this.descuentoRembolso = descuentoRembolso;
    }

    public Boolean getPagoExtra() {
        return pagoExtra;
    }

    public void setPagoExtra(Boolean pagoExtra) {
        this.pagoExtra = pagoExtra;
    }

    public Date getFechaPagoExtra() {
        return fechaPagoExtra;
    }

    public void setFechaPagoExtra(Date fechaPagoExtra) {
        this.fechaPagoExtra = fechaPagoExtra;
    }

    public Double getCantidadPagoExtra() {
        return cantidadPagoExtra;
    }

    public void setCantidadPagoExtra(Double cantidadPagoExtra) {
        this.cantidadPagoExtra = cantidadPagoExtra;
    }

    public Boolean getValGenerarPagos() {
        return valGenerarPagos;
    }

    public void setValGenerarPagos(Boolean valGenerarPagos) {
        this.valGenerarPagos = valGenerarPagos;
    }

    public Predio getPredioConstruido() {
        return predioConstruido;
    }

    public void setPredioConstruido(Predio predioConstruido) {
        this.predioConstruido = predioConstruido;
    }

    public Boolean getValidadorBotonImagen() {
        return validadorBotonImagen;
    }

    public void setValidadorBotonImagen(Boolean validadorBotonImagen) {
        this.validadorBotonImagen = validadorBotonImagen;
    }

    public String getImagenMostrar() {
        return imagenMostrar;
    }

    public void setImagenMostrar(String imagenMostrar) {
        this.imagenMostrar = imagenMostrar;
    }

    public BucketController getControladorBucket() {
        return controladorBucket;
    }

    public void setControladorBucket(BucketController controladorBucket) {
        this.controladorBucket = controladorBucket;
    }

    public CambiaLoteController getCambiaLoteController() {
        return cambiaLoteController;
    }

    public void setCambiaLoteController(CambiaLoteController cambiaLoteController) {
        this.cambiaLoteController = cambiaLoteController;
    }

    public DevolucionAbonoController getDevolucionAbonoController() {
        return devolucionAbonoController;
    }

    public void setDevolucionAbonoController(DevolucionAbonoController devolucionAbonoController) {
        this.devolucionAbonoController = devolucionAbonoController;
    }

    public GenericValidationController getGenericController() {
        return genericController;
    }

    public void setGenericController(GenericValidationController genericController) {
        this.genericController = genericController;
    }        

}
