package com.ejie.ad74a.service.impl;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ejie.ad74a.dao.PagosBeneficiarioDao;
import com.ejie.ad74a.dao.PagosDao;
import com.ejie.ad74a.dao.PrestadorServicioDao;
import com.ejie.ad74a.dao.SolicitantesDao;
import com.ejie.ad74a.dao.SoporteDao;
import com.ejie.ad74a.dao.TarifasDao;
import com.ejie.ad74a.model.Pagos;
import com.ejie.ad74a.model.PagosBeneficiario;
import com.ejie.ad74a.model.PrestadorServicio;
import com.ejie.ad74a.model.Solicitantes;
import com.ejie.ad74a.model.Soporte;
import com.ejie.ad74a.model.Tarifas;
import com.ejie.ad74a.service.PagosService;
import com.ejie.ad74a.util.UtilDates;

/**
 * PagosServiceImpl generated by UDA, 15-sep-2023 12:25:35.
 * 
 * @author UDA
 */

@Service(value = "pagosService")
public class PagosServiceImpl implements PagosService {

	private static final int TRANSACTION_TIMEOUT = 9999999;

	private static final Logger logger = LoggerFactory.getLogger(PagosServiceImpl.class);

	@Autowired
	private PagosDao pagosDao;

	@Autowired
	private PrestadorServicioDao prestadorServicioDao;

	@Autowired
	private TarifasDao tarifasDao;

	@Autowired
	private SolicitantesDao solicitantesDao;

	@Autowired
	private PagosBeneficiarioDao pagosBeneficiarioDao;
	
	@Autowired
	private SoporteDao soporteDao;

	/**
	 * Inserts a single row in the Pagos table.
	 *
	 * @param pagos Pagos
	 * @return Pagos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Pagos add(Pagos pagos) {
		if (pagos != null) {
			pagos.setUsuAlta74("AD74A");
//			pagos.setUsuModif74(UtilSeguridad.getNameUsuarioConectado());
			pagos.setfAlta74(UtilDates.getFechaSistema());
//			pagos.setfModif74(UtilidadesFecha.getFechaSistema());
		}
		return this.pagosDao.add(pagos);
	}

	/**
	 * M�todo para a�adir un nuevo pago
	 * 
	 * @param pagos Pagos
	 * @return Pagos
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class, timeout = PagosServiceImpl.TRANSACTION_TIMEOUT)
	public Pagos anadirPago(Pagos pagos) {

		// Fechas inicio y fin de pantalla
		Calendar c1 = Calendar.getInstance();
		c1.set(Calendar.MONTH, Integer.parseInt(pagos.getMesPago74()) - 1);
		c1.set(Calendar.DAY_OF_MONTH, 1);
		c1.set(Calendar.YEAR, pagos.getAnyoPago74().intValue());
		c1.set(Calendar.HOUR_OF_DAY, 0);
		c1.set(Calendar.MINUTE, 0);
		c1.set(Calendar.SECOND, 0);
		c1.set(Calendar.MILLISECOND, 0);
		Date fechaIniCalculoPantalla = c1.getTime();

		Calendar c2 = c1;
		c2.set(Calendar.DAY_OF_MONTH, c2.getActualMaximum(Calendar.DAY_OF_MONTH));
		Date fechaFinCalculoPantalla = c2.getTime();

		// Buscar los prestadores de servicio del a�o y mes para hacer los calculos
		PrestadorServicio ps = new PrestadorServicio();
		ps.setfInicioServicio73(fechaIniCalculoPantalla);
		List<PrestadorServicio> prestadoresServicioList = this.prestadorServicioDao
				.findPrestadorServicioEntreFechas(ps);

		// Calculamos para cada uno sus datos(periodo). Guardamos fInicio y fFin.
		for (PrestadorServicio prestador : prestadoresServicioList) {

			// Si no llega dato de prestador en pagos o si llega y es igual alde la
			// iteraci�n, hacer el c�lculo
			if (pagos.getPrestadorServicio() == null || pagos.getPrestadorServicio().getIdPrestador73() == null
					|| (pagos.getPrestadorServicio().getIdPrestador73() != null
							&& pagos.getPrestadorServicio().getIdPrestador73().compareTo(prestador.getIdPrestador73()) == 0)) {

				// Calcular fInicio y fFin del prestador de servicio para hacer los c�lculos.
				Date fInicio = prestador.getfInicioServicio73().compareTo(fechaIniCalculoPantalla) > 0
						? prestador.getfInicioServicio73()
						: fechaIniCalculoPantalla;
						
				Date fFin = fechaFinCalculoPantalla;
				if(prestador.getfFinServicio73() != null && prestador.getfFinServicio73().compareTo(fechaFinCalculoPantalla) < 0) {
					fFin = prestador.getfFinServicio73();
				}

				// Buscar las tarifas que se aplican al periodo aplicado para cada uno de
				// los niveles de soporte de beneficiario definidos.
				// Guardar idNivel, FNivDesde, FNivHasta, NTarifa (lo llamamos A)
				List<Tarifas> tarifasList = this.tarifasDao.findTarifasPorNivelSoporteEntreFechas(fInicio, fFin);

				// Buscar BENEFICIARIOS que:
				// - Tenga fecha instalacion y sea <= fFin
				// - Que no tenga fecha baja y si la tiene que sea > fInicio.
				List<Solicitantes> beneficiariosList = this.solicitantesDao.findBeneficiariosPago(fInicio, fFin);

				// Dar de alta registros en pagos.
				Pagos pago = new Pagos();
				pago.setPrestadorServicio(prestador);
				pago.setAnyoPago74(pagos.getAnyoPago74());
				pago.setMesPago74(pagos.getMesPago74());
				Pagos newPago = this.add(pago);

				// Por cada uno de ellos añadir registros a PAGOS_BENEFICIARIO como tarifas
				// aplicadas.
				logger.debug("Total de beneficiarios a tratar: {}", beneficiariosList.size());
				
				// Para probar el proceso limitamos a 10 beneficiarios
//				for (Solicitantes beneficiario : beneficiariosList.subList(0, 10)) {
				for (Solicitantes beneficiario : beneficiariosList) {

					// Calcular fecha desde (fecha de instalaci�n > FInicio y <= FFin, ser� la
					// fecha
					// de instalaci�n. En caso contrario, ser� FInicio)
					Date fechaDesde = beneficiario.getfInicio01().compareTo(fInicio) > 0
							&& beneficiario.getfInicio01().compareTo(fFin) <= 0 ? beneficiario.getfInicio01() : fInicio;

					// Calcular fecha hasta (si no tiene fecha de baja, ser� FFin. Si tiene fecha
					// de baja, ser� el m�ximo entre la fecha de baja y FFin.)
					Date fechaHasta;
					if (beneficiario.getfBajaPs01() == null) {
						fechaHasta = fFin;
					} else {
						if (beneficiario.getfBajaPs01().compareTo(fFin) < 0) {
							fechaHasta = beneficiario.getfBajaPs01();
						} else {
							fechaHasta = fFin;
						}
					}
					
					// Buscar los niveles de soporte que ha tenido el beneficiario desde la fechaDesde
					Soporte soporteAux = new Soporte();
					soporteAux.setIdSolicitante22(beneficiario.getIdSolicitante01());
					List<Soporte> listaSoportes = this.soporteDao.findSoporte(soporteAux, fechaDesde, fechaHasta);
					
					Date fFacHastaAux = null;
					// Recoremos los soportes del beneficiario y con cada una de ellos:
					// - Buscamos en A las tarifas que dicho nivel (B) aplica en ese periodo.
					List<Tarifas> tarifasListNivelesSoporte = new ArrayList<>();
					for (Soporte soporte : listaSoportes) {
						boolean cambiaFechaHasta = false;
						Date fechaHastaAux = null;
						//Pasar las fechas a formato correcto. Sino no hace bien la resta de días en algunos casos
						//Cuando va a insertar los pagos del beneficiario.
						try {
							fechaDesde = UtilDates.formatearDateToDateFormat(fechaDesde);
							fechaHasta = UtilDates.formatearDateToDateFormat(fechaHasta);
							soporte.setfAlta22(UtilDates.formatearDateToDateFormat(soporte.getfAlta22()));
							soporte.setfHistorico22(UtilDates.formatearDateToDateFormat(soporte.getfHistorico22()));		
						} catch (ParseException e) {
							logger.error("Error en formateo de fechas-->", e.getLocalizedMessage());
						}
						
						for (Tarifas tarifa : tarifasList) {
							
							if (!cambiaFechaHasta) {
								fechaHastaAux = fechaHasta;
							}
							
							if(soporte.getfHistorico22() == null) {
								cambiaFechaHasta = false;
								soporte.setfHistorico22(fechaHastaAux);
							}else {
								fechaHastaAux = soporte.getfHistorico22();
								cambiaFechaHasta = true;
							}
							
							if (soporte.getIdNivelSoporte22().compareTo(tarifa.getMaestroNivSoporte().getIdNivelSoporte58()) == 0
									&& tarifa.getfVigenciaHasta76().compareTo(soporte.getfAlta22()) >= 0
									&& (tarifa.getfVigenciaHasta76() == null || tarifa.getfVigenciaDesde76().compareTo(soporte.getfHistorico22()) <= 0)) {
								
								// Calculamos la fehca desde y hasta para la tarifa
								Date fFacDesde = null;
								if (fFacHastaAux == null) {
									if (tarifa.getfVigenciaDesde76().compareTo(fechaIniCalculoPantalla) <= 0) {
										fFacDesde = fechaDesde;
									}
								}else {
									fFacDesde = fFacHastaAux;
								}
								
								Date fFacHasta;
								if (fechaHastaAux != null) {
									if (cambiaFechaHasta) {
										fFacHasta = fechaHastaAux;
									}else {
										fFacHasta = fechaHasta;
									}
									
								}else {
									fFacHasta = tarifa.getfVigenciaHasta76().compareTo(fechaFinCalculoPantalla) >= 0
											? fechaFinCalculoPantalla
											: tarifa.getfVigenciaHasta76();
								}
								
								tarifa.setfDesde(fFacDesde);
								tarifa.setfHasta(fFacHasta);								
								tarifasListNivelesSoporte.add(tarifa);
								
								if (fFacHasta.compareTo(fechaHasta) <= 0) {
									fFacHastaAux = fFacHasta;
								}
							}
						}
					}

					// - Recorremos los elementos de A seleccionados y con cada uno de ellos
					// calculamos el n�ero de d�as a aplicar: 
					for (Tarifas tarifa : tarifasListNivelesSoporte) {
						//Si la fecha de facturación hasta supera la fecha fin (fin de mes) la fecha de facturación hasta solo puede ser fin de mes a facturar..
						if (tarifa.getfHasta() != null && tarifa.getfHasta().compareTo(fFin) > 0) {
							tarifa.setfHasta(fFin);
						}
						
						Long n = null;
						//Si la prestadora ha dado de baja al beneficiario el d�a de la baja no se tiene en cuenta
						if (beneficiario.getfBajaPs01() != null && beneficiario.getfBajaPs01().compareTo(fechaHasta) <= 0) {
							n = TimeUnit.DAYS.convert(tarifa.getfHasta().getTime() - tarifa.getfDesde().getTime(), TimeUnit.MILLISECONDS);
						}else {
							//Si la fecha hasta facturable es menor que la fecha fin (del mes facturable) se cuenta un día menos
							if (tarifa.getfHasta() != null && tarifa.getfHasta().compareTo(fFin) < 0) {
								n = TimeUnit.DAYS.convert(tarifa.getfHasta().getTime() - tarifa.getfDesde().getTime(), TimeUnit.MILLISECONDS);
							}else {
									n = TimeUnit.DAYS.convert(tarifa.getfHasta().getTime() - tarifa.getfDesde().getTime(), TimeUnit.MILLISECONDS)
											+ 1;
							}	
						}
						//Cuando coincide que el cálculo de días es cero no debe insertar el registro.
						if (n > 0) {
							PagosBeneficiario pagoBeneficiario = new PagosBeneficiario();
							pagoBeneficiario.setSolicitantes(beneficiario);
							pagoBeneficiario.setPagos(newPago);
							pagoBeneficiario.setNumDias75(n);
							pagoBeneficiario.setMaestroNivSoporte(tarifa.getMaestroNivSoporte());
							pagoBeneficiario.setTarifa75(tarifa.getTarifa76());
							pagoBeneficiario.setUsuAlta75("AD74A");
							pagoBeneficiario.setfAlta75(UtilDates.getFechaSistema());
							this.pagosBeneficiarioDao.add(pagoBeneficiario);
						}	
					}
				}
			}
		}

		return pagos;

	}

	/**
	 * Método para añadir un nuevo pago
	 * 
	 * @param pagos Pagos
	 * @return Pagos
	 * @throws Exception 
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class, timeout = PagosServiceImpl.TRANSACTION_TIMEOUT)
	public Pagos recalcularPago(Pagos pagos) throws Exception {
		// Si en el mismo mes han prestado servicio varias empresas, existirá un
		// registro en TPagos para cada una. Se recalculará el seleccionado.

		// Buscar si existe prestador o no está activo el mes tratado
		Pagos pagosAux = this.pagosDao.find(pagos);

		// Algoritmo:
		// 1.- Comprobar que es la �ltima mensualidad
		// 2.- Si no es �ltima mensualidad no hacer nada y finalizar proceso
		// 3.- Si es la �ltima mensaualidad:
		// 3.1.- Borrar todos los datos
		// 3.2.- Si el prestador no est� activo o no se encuantran datos: no hacer nada
		// m�s
		// 3.3.- En caso contrario se borrar�n tambi�n pero se generar�n nuevos pagos

		// 1.- Comprobar si es la �ltima mensualidad
		Pagos ultimoPago = this.pagosDao.obtenerUltimaFechaPago();
		if (pagosAux != null && ultimoPago.getAnyoPago74().compareTo(pagosAux.getAnyoPago74()) == 0 && ultimoPago.getMesPago74().compareTo(pagosAux.getMesPago74())==0) {
			// 3.- Es la �ltima mensualidad. Comprobar si el prestador est� activo en esas
			// fechas
			Calendar calendar = Calendar.getInstance();
			calendar.clear();
			calendar.set(Calendar.MONTH, Integer.valueOf(pagosAux.getMesPago74()) - 1);
			calendar.set(Calendar.YEAR, pagosAux.getAnyoPago74().intValue());
			Date mensDate = calendar.getTime();

			// 3.1.- Borrar todos los datos
			PagosBeneficiario pagosBeneficiario = new PagosBeneficiario();
			pagosBeneficiario.setPagos(pagosAux);
			List<PagosBeneficiario> pagosBeneficiarioList = this.pagosBeneficiarioDao.findAllLike(pagosBeneficiario,
					null, false);

			// Borrar pagos beneficiario
			for (PagosBeneficiario pagoBeneficiario : pagosBeneficiarioList) {
				this.pagosBeneficiarioDao.remove(pagoBeneficiario);
			}

			// Borrar el pago
			this.pagosDao.remove(pagosAux);

			// 3.2.- Generar nuevo pago
			if (pagosAux.getPrestadorServicio() != null && pagosAux.getPrestadorServicio().getIdPrestador73() != null
					&& pagosAux.getPrestadorServicio().getfInicioServicio73().compareTo(mensDate) <= 0
					&& pagosAux.getPrestadorServicio().getfFinServicio73().compareTo(mensDate) >= 0) {
				Pagos pagoAnadir = new Pagos();
				pagoAnadir.setAnyoPago74(pagosAux.getAnyoPago74());
				pagoAnadir.setMesPago74(pagosAux.getMesPago74());
				// Se necesita el prestador para solo a�adir los pagos a �l ya que puede haber
				// m�s de un PS en la misma mensualidad
				pagoAnadir.setPrestadorServicio(pagosAux.getPrestadorServicio());
				this.anadirPago(pagoAnadir);
			}
			// 3.3.- No hacer nada m�s

		} else {
			// 2.- No es la �ltima mensualidad as� que no se hace nada o el pago no existe
			String mensaje = "";
			if(pagosAux == null) {
				logger.debug("El pago que se quiere recalcular (idPago74={}) no existe", pagos.getIdPago74());
				mensaje = "El pago que se quiere recalcular (idPago74="+pagos.getIdPago74()+") no existe";
			}else {				
				logger.debug(
						"No se ha realizado ninguna operaci�n por no ser la �ltima mensualidad. �ltima mensualidad: {}-{}",
						ultimoPago.getAnyoPago74(), ultimoPago.getMesPago74());
				mensaje = "No se ha realizado ninguna operaci�n por no ser la �ltima mensualidad. �ltima mensualidad: "+ultimoPago.getAnyoPago74()+"-"+ultimoPago.getMesPago74();
			}
			
			throw new Exception(mensaje);
		}

		return pagosAux;
	}
}
