package com.mx.dla.dda.reporte.amortizacion.bos;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mx.dla.dda.admin.catalogos.estudios.daos.CatalogoEstudiosDAO;
import com.mx.dla.dda.admin.catalogos.estudios.dtos.CatalogoEstudioDTO;
import com.mx.dla.dda.contrato.terminos.dtos.ContratoConsultaDTO;
import com.mx.dla.dda.reporte.amortizacion.daos.ReporteAmortizacionDAO;
import com.mx.dla.dda.reporte.amortizacion.dtos.RegistroMontoDTO;
import com.mx.dla.dda.reporte.amortizacion.dtos.RegistroRubroDTO;
import com.mx.dla.global.bos.BaseBO;

@Service
public class ReporteAmortizacionBO extends BaseBO{

	@Autowired
	private ReporteAmortizacionDAO reporteAmortizacionDAO;
	
	@Autowired
	private CatalogoEstudiosDAO catalogoEstudiosDAO;

	public List<RegistroRubroDTO> obtenListaMontosTotalesContratoPorEstudio(
			Long idEstudio, String fecha, Long mesesSearch) throws ParseException {

		List<RegistroRubroDTO> totalesPorContrato = new ArrayList<RegistroRubroDTO>();

		List<ContratoConsultaDTO> contratosPorEstudio = reporteAmortizacionDAO
				.consultaContratosConAmoritzacionPorEstudio(idEstudio);
		
		List<String> meses = getFechasAConsultar(fecha);

		for (ContratoConsultaDTO contrato : contratosPorEstudio) {

			Long idContrato = contrato.getIdContrato();
			String numContrato = contrato.getNumeroContrato();

			List<RegistroMontoDTO> montosTotalesContrato = reporteAmortizacionDAO
					.obtenMontosTotalesContrato(idContrato, fecha, mesesSearch);
			
			List<RegistroMontoDTO> list = obtenMontosMesesVacios(meses);
			
			//completaMontosTotales(montosTotalesContrato);

			if (montosTotalesContrato != null) {
				
				for(RegistroMontoDTO item : montosTotalesContrato)
				    buscaRegistroMontoMes(list, item);

				RegistroRubroDTO totalContrato = new RegistroRubroDTO();
				totalContrato.setId(idContrato);
				totalContrato.setDescripcion(numContrato);
				totalContrato.setMontoMes(list);
				totalContrato.setFechaFin(contrato.getFechaFin());
				totalContrato.setFechainicio(contrato.getFechaInicio());
				totalContrato.setCodigoSAP("-");
				totalContrato.setSistema(contrato.getSistema());
				totalContrato.setIdBroadview("-");
				totalesPorContrato.add(totalContrato);
			}

		}

		return totalesPorContrato;
	}
	
	public RegistroRubroDTO obtenMontosTotalesPorEstudio(Long idEstudio,
			String fecha) throws ParseException {

		List<RegistroMontoDTO> montosTotalesEstudio = reporteAmortizacionDAO
				.obtenMontosTotalesEstudio(idEstudio, fecha);
		
		CatalogoEstudioDTO nombreEstudio = catalogoEstudiosDAO.catalogoEstudiosPorId(idEstudio.intValue());
		
		List<String> meses = getFechasAConsultar(fecha);

		List<RegistroMontoDTO> list = obtenMontosMesesVacios(meses);
		
		for(RegistroMontoDTO item : montosTotalesEstudio)
		    buscaRegistroMontoMes(list, item);
				
		RegistroRubroDTO totalesEstudio = new RegistroRubroDTO();
		totalesEstudio.setId(idEstudio);
		totalesEstudio.setDescripcion(nombreEstudio.getDescripcionEstudio());
		totalesEstudio.setMontoMes(list);

		return totalesEstudio;
	}
	
	public RegistroRubroDTO obtenMontosTotalesPorEstudioDLASAP(Long idEstudio,
			String fecha) throws ParseException {

		List<RegistroMontoDTO> montosTotalesEstudio = reporteAmortizacionDAO
				.obtenMontosTotalesEstudio(idEstudio, fecha);
		
		CatalogoEstudioDTO nombreEstudio = catalogoEstudiosDAO.catalogoEstudiosPorId(idEstudio.intValue());
		
		List<String> meses = getFechasAConsultar(fecha);

		List<RegistroMontoDTO> list = obtenMontosMesesVacios(meses);
		
		for(RegistroMontoDTO item : montosTotalesEstudio)
		    buscaRegistroMontoMes(list, item);
				
		RegistroRubroDTO totalesEstudio = new RegistroRubroDTO();
		totalesEstudio.setId(idEstudio);
		totalesEstudio.setDescripcion(nombreEstudio.getDescripcionEstudio());
		totalesEstudio.setMontoMes(list);

		return totalesEstudio;
	}
	
	private void addMontoEstudio(RegistroRubroDTO princ, Map<String, List<RegistroRubroDTO>> totalesPorContratoSAPDLA, 
			int place, Long idEstudio, String fecha, String sistema, Integer month) throws ParseException {
		if (Objects.nonNull(princ)) {
			RegistroRubroDTO ant = obtenMontosTotalesPorEstudio(idEstudio, fecha);
			RegistroRubroDTO sig = obtenMontosTotalesPorEstudioSAP(idEstudio, fecha);
			princ.setSistema(sistema);
			princ.setAcumuladoAnterior(0.0);
			princ.setAcumuladoPosterior(0.0);
			princ.setId(-1L);
			if (Objects.nonNull(ant)) {
				princ.setAcumuladoAnterior(ant.getMontoMes().get(12).getMonto());
			}
			if (Objects.nonNull(sig)) {
				princ.setAcumuladoPosterior(sig.getMontoMes().get(12).getMonto());
			}
			Double r1 = princ.getAcumuladoAnterior();
			Double r2 = princ.getAcumuladoPosterior();
			int mon = 1;
			for (RegistroMontoDTO mnt : princ.getMontoMes()){
				if(mon <= month) {
					r1 += mnt.getMonto();
				}
				if(mon > month && mon <= 12 ) {
					r2 += mnt.getMonto();
				}
				mon++;
			}
			princ.setRemanente(r1 - r2);
			princ.setIdBroadview("-");
			princ.setCodigoSAP("-");
			totalesPorContratoSAPDLA.get(idEstudio.toString()).add(place, princ);
		} else {
			RegistroRubroDTO emptyValue = new RegistroRubroDTO();
			List<String> meses = getFechasAConsultar(fecha);
			emptyValue.setMontoMes(obtenMontosMesesVacios(meses));
			emptyValue.setSistema(sistema);
			emptyValue.setCodigoSAP("-");
			emptyValue.setIdBroadview("-");
			emptyValue.setAcumuladoAnterior(0.0);
			emptyValue.setAcumuladoPosterior(0.0);
			emptyValue.setRemanente(0.0);
			totalesPorContratoSAPDLA.get(idEstudio.toString()).add(emptyValue);
		}
	}
	
	public Map<String, List<RegistroRubroDTO>> obtenListaMontosTotalesContratoPorEstudioDLASAP(
			Long idEstudio, String fecha, Integer year, Integer month) throws ParseException {
		RegistroRubroDTO totalesMontosEstudio = obtenMontosTotalesPorEstudio(idEstudio, fecha);
		RegistroRubroDTO totalesMontosEstudioSAP = obtenMontosTotalesPorEstudioSAP(idEstudio, fecha);
		Map<String, List<RegistroRubroDTO>> totalesPorContratoSAPDLA = new LinkedHashMap<>();
		totalesPorContratoSAPDLA.put(idEstudio.toString(), new ArrayList<RegistroRubroDTO>());
		addMontoEstudio(totalesMontosEstudio, totalesPorContratoSAPDLA, 0, idEstudio, "01/" + (year - 1), "DDA", month);
		addMontoEstudio(totalesMontosEstudioSAP, totalesPorContratoSAPDLA, 0, idEstudio, "01/" + (year + 1), "SAP", month);
		totalesPorContratoSAPDLA.putAll(getListResult(idEstudio, fecha));
		Map<String, List<RegistroRubroDTO>> totalesPorContratoAnioAnterior = getListResult(idEstudio, "01/" + (year - 1));
		Map<String, List<RegistroRubroDTO>> totalesPorContratoAnioSiguiente = getListResult(idEstudio, "01/" + (year + 1));
		for(Entry<String, List<RegistroRubroDTO>> total :totalesPorContratoSAPDLA.entrySet()) {
			if(total.getValue().size() < 2) {
				RegistroRubroDTO emptyValue = new RegistroRubroDTO();
				List<String> meses = getFechasAConsultar(fecha);
				emptyValue.setMontoMes(obtenMontosMesesVacios(meses));
				emptyValue.setSistema(total.getValue().get(0).getSistema().equals("DDA") ? "SAP" : "DDA");
				emptyValue.setFechainicio(total.getValue().get(0).getFechainicio());
				emptyValue.setFechaFin(total.getValue().get(0).getFechaFin());
				emptyValue.setCodigoSAP(total.getValue().get(0).getCodigoSAP());
				emptyValue.setIdBroadview(total.getValue().get(0).getIdBroadview());
				total.getValue().add(emptyValue);
			}
			
			for (int i = 0; total.getValue().size() > i; i++) {
				if(!total.getKey().equals(idEstudio.toString())) {
					if(totalesPorContratoAnioAnterior.get(total.getKey()).size() > i) {
						if(Objects.nonNull(totalesPorContratoAnioAnterior.get(total.getKey()))) {
							total.getValue().get(i)
							.setAcumuladoAnterior(totalesPorContratoAnioAnterior
									.get(total.getKey()).get(i).getMontoMes().get(12).getMonto());
						} else {
							total.getValue().get(i).setAcumuladoAnterior(0.0);
						}
						if(Objects.nonNull(totalesPorContratoAnioSiguiente.get(total.getKey()))) {
							total.getValue().get(i)
							.setAcumuladoPosterior(totalesPorContratoAnioSiguiente
									.get(total.getKey()).get(i).getMontoMes().get(12).getMonto());
						}else {
							total.getValue().get(i).setAcumuladoPosterior(0.0);
						}
						Double r1 = total.getValue().get(i).getAcumuladoAnterior();
						Double r2 = total.getValue().get(i).getAcumuladoPosterior();
						int mon = 1;
						for (RegistroMontoDTO mnt : total.getValue().get(i).getMontoMes()){
							if(mon <= month) {
								r1 += mnt.getMonto();
							}
							if(mon > month && mon <= 12 ) {
								r2 += mnt.getMonto();
							}
							mon++;
						}
						total.getValue().get(i).setRemanente(r1 - r2);
					} else {
						total.getValue().get(i).setAcumuladoAnterior(0.0);
						total.getValue().get(i).setAcumuladoPosterior(0.0);
						total.getValue().get(i).setRemanente(0.0);
					}
				}
			}
		}
		return totalesPorContratoSAPDLA;
	}
	
	public Map<String, List<RegistroRubroDTO>> getListResult(Long idEstudio, String fecha) throws ParseException {
		List<RegistroRubroDTO> totalesPorContrato = obtenListaMontosTotalesContratoPorEstudio(idEstudio, fecha, 11L);
		List<RegistroRubroDTO> totalesPorContratoSAP = obtenListaMontosTotalesContratoPorEstudioSAP(idEstudio, fecha, 11L);
		Map<String, List<RegistroRubroDTO>> totalesPorContratoSAPDLA = new LinkedHashMap<>();
		totalesPorContrato.addAll(totalesPorContratoSAP);
		for(RegistroRubroDTO total :totalesPorContrato) {
			if(!totalesPorContratoSAPDLA.containsKey(total.getDescripcion())) {
				totalesPorContratoSAPDLA.put(total.getDescripcion(), new ArrayList<RegistroRubroDTO>());
			}
			totalesPorContratoSAPDLA.get(total.getDescripcion()).add(total);
		}
		
		return totalesPorContratoSAPDLA;
	}
	
	public List<String> getFechasAConsultar(String fechaIni)
			throws ParseException {

		List<String> fechas = new ArrayList<String>();
		DateFormat formato = new SimpleDateFormat("MM/yyyy");

		Date date = formato.parse(fechaIni);
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		String fecha = formato.format(cal.getTime());

		fechas.add(fecha);

		for (int i = 1; i < 12; i++) {

			cal.add(Calendar.MONTH, 1);

			fecha = formato.format(cal.getTime());
			fechas.add(fecha);
		}

		return fechas;
	}
	
	public List<RegistroMontoDTO> obtenMontosMesesVacios(List<String> meses)
			throws ParseException {

		List<RegistroMontoDTO> response = new ArrayList<RegistroMontoDTO>();
		DateFormat formato = new SimpleDateFormat("MM/yyyy");

		for (String mes : meses) {

			RegistroMontoDTO newItem = new RegistroMontoDTO();
			Date fecha = formato.parse(mes);

			newItem.setFecha(fecha);
			newItem.setMonto(0.0);
			response.add(newItem);

		}
		
		RegistroMontoDTO newItem = new RegistroMontoDTO();
		newItem.setFecha(null);
		newItem.setMonto(0.0);
		response.add(newItem);

		return response;
	}
	
	private void buscaRegistroMontoMes(List<RegistroMontoDTO> montos,
			RegistroMontoDTO actualizaMonto) throws ParseException {

		Double parcial = 0.0;
		RegistroMontoDTO total = null;

		for (RegistroMontoDTO monto : montos)
		{				
			if (monto != null)
				if (monto.getFecha() != null) {
					if (monto.getFecha().equals(actualizaMonto.getFecha())) {
						parcial = actualizaMonto.getMonto();
						monto.setMonto(parcial);
					}
				} else {
					total = monto;
				}
		}

		if (total != null)
			total.setMonto(truncateTo(total.getMonto() + parcial, 2));

	}
	
	static double truncateTo( double unroundedNumber, int decimalPlaces ){
	    int truncatedNumberInt = (int)( unroundedNumber * Math.pow( 10, decimalPlaces ) );
	    double truncatedNumber = (double)( truncatedNumberInt / Math.pow( 10, decimalPlaces ) );
	    return truncatedNumber;
	}

	public List<RegistroRubroDTO> obtenListaMontoTitulosPorContrato(
			Long idContrato, String fecha) throws ParseException {

		List<RegistroMontoDTO> montosTitulosContrato = reporteAmortizacionDAO
				.obtenMontosTitulosContrato(idContrato, fecha);

		List<RegistroRubroDTO> montosTitulos = new ArrayList<RegistroRubroDTO>();
		montosTitulos = separaMensualmente(montosTitulosContrato, fecha);

		return montosTitulos;

	}	
	
	private List<RegistroRubroDTO> separaMensualmente(
			List<RegistroMontoDTO> titulosMontos, String fecha)
			throws ParseException {

		List<RegistroRubroDTO> montoTitulos = new ArrayList<>();
		Map<String, List<RegistroMontoDTO>> map = new HashMap<>();
		List<String> meses = getFechasAConsultar(fecha);

		for (RegistroMontoDTO item : titulosMontos) 
		{
			String season = Objects.isNull(item.getTemporada()) ? "" : " Season " + 
					StringUtils.leftPad(item.getTemporada(), 2, "0") ;
			List<RegistroMontoDTO> list = map.get(item.getDescripcion() + season);
			if (list == null) {
				
				list = obtenMontosMesesVacios(meses);
				map.put(item.getDescripcion() + season, list);

				RegistroRubroDTO montoTituloRegistro = new RegistroRubroDTO();
				montoTituloRegistro.setTemporada(item.getTemporada());
				montoTituloRegistro.setDescripcion(item.getDescripcion() + season);
				montoTituloRegistro.setNombreUnico(item.getDescripcion());
				montoTituloRegistro.setMontoMes(list);
				montoTituloRegistro.setSistema(item.getSistema());
				montoTituloRegistro.setCodigoSAP(item.getIdSap());
				montoTituloRegistro.setIdBroadview(item.getIdBroadView());
				
				montoTitulos.add(montoTituloRegistro);
				logger.debug("id cto:" + item.getId() + " no tit:" + montoTitulos.size());
			}

			buscaRegistroMontoMes(list, item);			
		}

		return montoTitulos;
	}
	
	//Amortizacion
	public List<RegistroRubroDTO> obtenListaMontosTotalesContratoPorEstudioSAP(Long idEstudio, String fecha, Long mesesSearch) throws ParseException {

		List<RegistroRubroDTO> totalesPorContrato = new ArrayList<RegistroRubroDTO>();

		List<ContratoConsultaDTO> contratosPorEstudio = reporteAmortizacionDAO.consultaContratosConAmoritzacionPorEstudioSAP(idEstudio);
		
		List<String> meses = getFechasAConsultar(fecha);

		for (ContratoConsultaDTO contrato : contratosPorEstudio) {

			Long idContrato = contrato.getIdContrato();
			String numContrato = contrato.getNumeroContrato();

			List<RegistroMontoDTO> montosTotalesContrato = reporteAmortizacionDAO.obtenMontosTotalesContratoSAP(idContrato, fecha, mesesSearch);
			
			List<RegistroMontoDTO> list = obtenMontosMesesVacios(meses);
			
			//completaMontosTotales(montosTotalesContrato);

			if (montosTotalesContrato != null) {
				
				for(RegistroMontoDTO item : montosTotalesContrato)
				    buscaRegistroMontoMes(list, item);

				RegistroRubroDTO totalContrato = new RegistroRubroDTO();
				totalContrato.setId(idContrato);
				totalContrato.setDescripcion(numContrato);
				totalContrato.setMontoMes(list);
				totalContrato.setFechaFin(contrato.getFechaFin());
				totalContrato.setFechainicio(contrato.getFechaInicio());
				totalContrato.setCodigoSAP("-");
				totalContrato.setSistema(contrato.getSistema());
				totalContrato.setIdBroadview("-");
				totalesPorContrato.add(totalContrato);
			}

		}

		return totalesPorContrato;
	}
	
	public RegistroRubroDTO obtenMontosTotalesPorEstudioSAP(Long idEstudio, String fecha) throws ParseException {

		List<RegistroMontoDTO> montosTotalesEstudio = reporteAmortizacionDAO.obtenMontosTotalesEstudioSAP(idEstudio, fecha);
		
		CatalogoEstudioDTO nombreEstudio = catalogoEstudiosDAO.catalogoEstudiosPorId(idEstudio.intValue());
		
		List<String> meses = getFechasAConsultar(fecha);

		List<RegistroMontoDTO> list = obtenMontosMesesVacios(meses);
		
		for(RegistroMontoDTO item : montosTotalesEstudio)
		    buscaRegistroMontoMes(list, item);
				
		RegistroRubroDTO totalesEstudio = new RegistroRubroDTO();
		totalesEstudio.setId(idEstudio);
		totalesEstudio.setDescripcion(nombreEstudio.getDescripcionEstudio());
		totalesEstudio.setMontoMes(list);

		return totalesEstudio;
	}

	public List<RegistroRubroDTO> obtenListaMontoTitulosPorContratoSAP(Long idContrato, String fecha) throws ParseException {

		List<RegistroMontoDTO> montosTitulosContrato = reporteAmortizacionDAO.obtenMontosTitulosContratoSAP(idContrato, fecha);

		List<RegistroRubroDTO> montosTitulos = new ArrayList<RegistroRubroDTO>();
		montosTitulos = separaMensualmente(montosTitulosContrato, fecha);

		return montosTitulos;

	}	

}