package com.ejie.ad74a.test.process;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.ejie.ad74a.exception.BatchProcessException;
import com.ejie.ad74a.model.Copago;
import com.ejie.ad74a.model.Periodo;
import com.ejie.ad74a.model.Piso;
import com.ejie.ad74a.model.PrecioBeneficiario;
import com.ejie.ad74a.model.PrecioPublico;
import com.ejie.ad74a.model.Solicitante;
import com.ejie.ad74a.process.IProcess;
import com.ejie.ad74a.service.CopagoService;
import com.ejie.ad74a.util.UtilDates;

/**
 * Proceso para realizar pruebas de ejecucion de codigo.
 */
@Component(value = "pruebaProcessTest")
public class PruebaProcessTest implements IProcess {

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

	@Autowired
	CopagoService copService;
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.ad74a.process.IProcess#execute(java.lang.String[])
	 */
	@Override
	public void execute(String... args) throws BatchProcessException {
		logger.info("***** EJECUCION DE TESTS *****");

		testArgumentWhenMensualidadIsNull();
		testArgumentWhenMensualidadIsNotNull();

		//PRECIOS PUBLICOS
		//fInicio 1.1 fFin 2.1
		precioPublicofInicio11fFin21Test();
		
		//fInicio 1.1 fFin 2.2
		precioPublicofInicio11fFin22Test();
		
		//fInicio 1.2 fFin 2.1
		precioPublicofInicio12fFin21Test();
		
		//fInicio 1.2 fFin 2.2
		precioPublicofInicio12fFin22Test();
		
		
		//PRECIOS PRIVADOS
		//fInicio 1.1.1. fFin 2.1.1
		preciosPrivadosFInicio111FFin211Test();
		//fInicio 1.1.2. fFin 2.1.1
		preciosPrivadosFInicio112FFin211Test();
		//fInicio 1.2.1 fFin 2.1.1
		preciosPrivadosFInicio121FFin211Test();
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 2), null, LocalDate.of(2024, 3, 10), null);
		//fInicio 1.2.2 fFin 2.1.1
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 15), null, LocalDate.of(2024, 3, 10), null);
		
		//fInicio 1.1.1. fFin 2.1.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2023, 3, 1), null, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 3, 15));
		//fInicio 1.1.2. fFin 2.1.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2023, 1, 30), null, LocalDate.of(2024, 1, 30), LocalDate.of(2024, 3, 15));
		//fInicio 1.2.1 fFin 2.1.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 10), null, LocalDate.of(2024, 3, 11), LocalDate.of(2024, 3, 24));
		//fInicio 1.2.2 fFin 2.1.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 10), null, LocalDate.of(2024, 3, 10), LocalDate.of(2024, 3, 24));

		//fInicio 1.1.1. fFin 2.2.1
		//testPreciosPrivados(mensualidad, LocalDate.of(2023, 3, 1), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1));
		//fInicio 1.1.2. fFin 2.2.1
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 01, 01), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 5), LocalDate.of(2024, 3, 15));
		//fInicio 1.2.1 fFin 2.2.1
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 2), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 5), LocalDate.of(2024, 3, 15));
		//fInicio 1.2.2 fFin 2.2.1
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 2), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 15));
		
		//fInicio 1.1.1. fFin 2.2.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2023, 3, 1), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 1), null);
		//fInicio 1.1.2. fFin 2.2.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 01, 01), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 5), null);
		//fInicio 1.2.1 fFin 2.2.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 2), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 5), null);
		//fInicio 1.2.2 fFin 2.2.2
		//testPreciosPrivados(mensualidad, LocalDate.of(2024, 3, 2), LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 1), null);
		
		//LISTA PRECIOS PRIVADOS
		//testLstPreciosPrivados(mensualidad, LocalDate.of(2024, 1, 1), null, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 3, 14), LocalDate.of(2024, 3, 16), null);
		
		
	}

	private void testArgumentWhenMensualidadIsNull() {
		
		
		// Given
		String args[] = { "48" };
		// When
		String idProvincia = args[0];
		String anyoMes = args.length == 2 ? args[1] : UtilDates.getPreviousToCurrentYearMonth();
		Periodo mensualidad = new Periodo(anyoMes);
		
		LocalDate firstDayOfMonth = YearMonth.now().minusMonths(1).atDay(1);
		LocalDate lastDayOfMoth = YearMonth.now().minusMonths(1).atEndOfMonth();
		
		// Then
		Assert.isTrue(("48").equals(idProvincia), "Fail Test. Provincia incorrecta");
		Assert.isTrue(firstDayOfMonth.equals(mensualidad.getfInicio()), "Fail Test. Fecha Inicio incorrecta");
		Assert.isTrue(lastDayOfMoth.equals(mensualidad.getfFin()), "Fail Test. Fecha Fin incorrecta");
		
		logger.info("testArgumentWhenMensualidadIsNull --> OK");
	}
	
	private void testArgumentWhenMensualidadIsNotNull() {
				
		// Given
		String args[] = { "48", "202401" };
		
		// When
		String idProvincia = args[0];
		Periodo mensualidad = new Periodo(args[1]);
		
		// Then
		Assert.isTrue(("48").equals(idProvincia), "Fail Test. Provincia incorrecta");
		Assert.isTrue(LocalDate.of(2024, 1, 1).equals(mensualidad.getfInicio()), "Fail Test. Fecha Inicio incorrecta");
		Assert.isTrue(LocalDate.of(2024, 1, 31).equals(mensualidad.getfFin()), "Fail Test. Fecha Fin incorrecta");
		
		logger.info("testArgumentWhenMensualidadIsNotNull --> OK");
	}
	
	/**
	 * fInicio 1.1 Fecha alta anterior o igual al inicio del mes - fFin 2.1 Fecha baja null o mayor
	 * @param mensualidad
	 */
	private void precioPublicofInicio11fFin21Test(){
		BigDecimal expetedValue = BigDecimal.valueOf(4).setScale(4);
		BigDecimal value = getImportePrecioPublico(LocalDate.of(2024, 1, 10), LocalDate.of(2024, 4, 10), null, null);
		Assert.isTrue(expetedValue.equals(value), "Expected: "+ expetedValue + " Value:" + value);
		
		logger.info("precioPublicofInicio11fFin21Test --> OK");
	}
	
	/**
	 * fInicio 1.1 Fecha alta anterior o igual al inicio del mes - fFin 2.2 Fecha menor o igual que fecha fin del mes
	 * @param mensualidad
	 */
	private void precioPublicofInicio11fFin22Test(){
		BigDecimal expetedValue = BigDecimal.valueOf(3.8710).setScale(4);
		BigDecimal value = getImportePrecioPublico(LocalDate.of(2024, 1, 10), LocalDate.of(2024, 3, 31), null, null);
		Assert.isTrue(expetedValue.equals(value), "Expected: "+ expetedValue + " Value:" + value);
		
		logger.info("precioPublicofInicio11fFin22Test --> OK");
	}
	
	/**
	 * fInicio 1.2 Fecha alta posterior al inicio del mes - fFin 2.1 Fecha baja null o mayor
	 * @param mensualidad
	 */
	private void precioPublicofInicio12fFin21Test(){
		BigDecimal expetedValue = BigDecimal.valueOf(0.1290).setScale(4);
		BigDecimal value = getImportePrecioPublico(LocalDate.of(2024, 3, 31), null, null, null);
		Assert.isTrue(expetedValue.equals(value), "Expected: "+ expetedValue + " Value:" + value);
		
		logger.info("precioPublicofInicio12fFin21Test --> OK");
	}
	
	/**
	 * fInicio 1.2 Fecha alta posterior al inicio del mes - fFin 2.2 Fecha menor o igual que fecha fin del mes
	 * @param mensualidad
	 */
	private void precioPublicofInicio12fFin22Test(){
		BigDecimal expetedValue = BigDecimal.valueOf(0.1290).setScale(4);
		BigDecimal value = getImportePrecioPublico(LocalDate.of(2024, 3, 30), LocalDate.of(2024, 3, 31), null, null);
		Assert.isTrue(expetedValue.equals(value), "Expected: "+ expetedValue + " Value:" + value);
		
		logger.info("precioPublicofInicio12fFin22Test --> OK");
	}
	
	/**
	 * Lanzamiento de test para calculo de importes en precios publicos
	 * @param fInicio
	 * @param fBaja
	 * @param lstCopagosBeneficiario
	 * @param piso
	 * @return
	 */
	private BigDecimal getImportePrecioPublico(LocalDate fInicio, LocalDate fBaja, List<Copago> lstCopagosBeneficiario, Piso piso) {
		Periodo mensualidad = new Periodo("202403");

		Solicitante solicitante = new Solicitante();
		solicitante.setfInicio(fInicio);
		solicitante.setfBaja(fBaja);
		solicitante.setPrecioPublico(new PrecioPublico(null, null, null, BigDecimal.valueOf(4)));
		
		List<Copago> lstCopagos = copService.getLstCopagosPublicos(mensualidad, solicitante, lstCopagosBeneficiario, piso); 
		
		return  lstCopagos.stream()
				.map(Copago::getImporte)
				.reduce(BigDecimal.ZERO, BigDecimal::add);
	}
	
	/**
	 * 1.1 Fecha alta solicitante anterior o igual al inicio del mes - 2.1 Fecha baja null o posterior al fin de mes
	 * 1.1.1 Fecha precio inicio ANTERIOR O IGUAL al inicio del mes - 2.1.1 Fecha precio fin POSTERIOR O IGUAL al fin de mes
	 */
	private void preciosPrivadosFInicio111FFin211Test () {
		
		BigDecimal expetedValue = BigDecimal.valueOf(2).setScale(4);
		BigDecimal value = this.getImportePrecioBeneficiario(LocalDate.of(2023, 1, 30), null, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 5, 1), null, null, null);
		Assert.isTrue(expetedValue.equals(value), "Expected: "+ expetedValue + " Value:" + value);
		
		logger.info("preciosPrivadosFInicio111FFin211 --> OK");
	}
	
	/**
	 * 1.1 Fecha alta solicitante anterior o igual al inicio del mes - 2.1 Fecha baja null o posterior al fin de mes
	 * 1.1.2 Fecha precio inicio POSTERIOR al inicio del mes - 2.1.1 Fecha precio fin POSTERIOR O IGUAL al fin de mes
	 */
	private void preciosPrivadosFInicio112FFin211Test () {
		
		BigDecimal expetedValue = BigDecimal.valueOf(1.4194).setScale(4);
		BigDecimal value = this.getImportePrecioBeneficiario(LocalDate.of(2023, 1, 30), null, LocalDate.of(2024, 3, 10),
				null, null, null, null);
		Assert.isTrue(expetedValue.equals(value), "Expected: " + expetedValue + " Value:" + value);
		
		logger.info("preciosPrivadosFInicio111FFin211 --> OK");
	}
	
	/**
	 * 1.2 Fecha alta posterior al inicio del mes - 2.1 Fecha baja null o posterior al fin de mes
	 * 1.1.2 Fecha precio inicio POSTERIOR al inicio del alta - 2.1.1 Fecha precio fin POSTERIOR O IGUAL al fin de mes
	 */
	private void preciosPrivadosFInicio121FFin211Test () {

		BigDecimal expetedValue = BigDecimal.valueOf(1.4194).setScale(4);
		BigDecimal value = this.getImportePrecioBeneficiario(LocalDate.of(2024, 3, 2), null, LocalDate.of(2024, 3, 10),
				null, null, null, null);
		Assert.isTrue(expetedValue.equals(value), "Expected: " + expetedValue + " Value:" + value);
		
		logger.info("preciosPrivadosFInicio111FFin211 --> OK");
	}
	
	/**
	 * Lanzamiento de test para calculo de importes de precios privados
	 * 
	 * @param fInicio
	 * @param fBaja
	 * @param fPrecioInicio1
	 * @param fPrecioFin1
	 * @param fPrecioInicio2
	 * @param fPrecioFin2
	 * @param pisoTutelado
	 * @return
	 */
	private BigDecimal getImportePrecioBeneficiario (LocalDate fInicio, LocalDate fBaja, LocalDate fPrecioInicio1, LocalDate fPrecioFin1,  LocalDate fPrecioInicio2, LocalDate fPrecioFin2, Piso pisoTutelado) {
		
		Periodo mensualidad = new Periodo("202403");
		
		Solicitante solicitante = new Solicitante();
		solicitante.setfInicio(fInicio);
		solicitante.setfBaja(fBaja);
		solicitante.setnPreciosBeneficiario(1);
		
		List<PrecioBeneficiario> lstPreciosBeneficiario = new ArrayList<>();
		
		if (null != fPrecioInicio1) {
			PrecioBeneficiario precio1 = new PrecioBeneficiario();
		
			precio1.setFechaInicio(fPrecioInicio1);
			precio1.setFechaFin(fPrecioFin1);
			precio1.setPrecioPrivado(BigDecimal.valueOf(2));
			precio1.setIndExento(Boolean.FALSE);
			lstPreciosBeneficiario.add(precio1);
		}
		
		if (null != fPrecioInicio2) {
			PrecioBeneficiario precio2 = new PrecioBeneficiario();
			
			precio2.setFechaInicio(fPrecioInicio2);
			precio2.setFechaFin(fPrecioFin2);
			precio2.setPrecioPrivado(BigDecimal.valueOf(1));
			precio2.setIndExento(Boolean.FALSE);
			
			lstPreciosBeneficiario.add(precio2);
		}
		
		solicitante.setLstPreciosBeneficiario(lstPreciosBeneficiario);

		List<Copago> lstCopagos = copService.getLstCopagosBeneficiario(mensualidad, solicitante, pisoTutelado); 
		
		return  lstCopagos.stream()
				.filter(copago -> !copago.isIndExento())
				.map(Copago::getImporte)
				.reduce(BigDecimal.ZERO, BigDecimal::add);
	}
	
}
