package com.mx.dla.dda.contrato.titulo.bos;

import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.mx.dla.dda.catalogos.daos.CatalogosDAO;
import com.mx.dla.dda.contrato.common.bos.CommonRestBO;
import com.mx.dla.dda.contrato.generales.daos.GeneralesDAO;
import com.mx.dla.dda.contrato.generales.dtos.ContratoDTO;
import com.mx.dla.dda.contrato.generales.dtos.FechasDTO;
import com.mx.dla.dda.contrato.generales.dtos.InfoDetalleContratoDTO;
import com.mx.dla.dda.contrato.generales.enums.Movimiento;
import com.mx.dla.dda.contrato.titulo.constants.TipoCambioTitulo;
import com.mx.dla.dda.contrato.titulo.daos.TituloDAO;
import com.mx.dla.dda.contrato.titulo.daos.TituloDmDAO;
import com.mx.dla.dda.contrato.titulo.dtos.AmortizacionSAP;
import com.mx.dla.dda.contrato.titulo.dtos.SapNum;
import com.mx.dla.dda.contrato.titulo.dtos.Titulo;
import com.mx.dla.dda.contrato.titulo.exception.TituloException;
import com.mx.dla.dda.contrato.titulo.rules.TituloRules;
import com.mx.dla.dda.contrato.titulos.dtos.ResponseValorTituloDTO;
import com.mx.dla.global.bos.BaseBO;

@Component
public class TitulosBussinesBO extends BaseBO{

	@Autowired
	protected CommonRestBO commonRestBO;
	
	@Autowired
	protected TituloDAO tituloDAO;
	
	@Autowired
	private TituloDmDAO tituloDmDAO;
	
	@Autowired
	protected GeneralesDAO generalesDAO;
	
	@Autowired
	protected CatalogosDAO catalogosDAO;
	
	@Autowired
	protected TituloRules tituloRules;
	
	@Autowired
	private TitulosUtilBO titulosUtilBO;
	
	DecimalFormat df = new DecimalFormat("#.00");
	
	/**
	 * @throws TituloException 
	 * @see Se verifica que un titulo cumpla las condiciones para ser notificado a sap, cuando estan en un contrato enmienda
	 */
	public void verificarEnmiendaActualizacion(Titulo titulo, String tipoCambio, ContratoDTO contrato) throws TituloException{		
		Titulo actual =  tituloDmDAO.buscarTitulo(titulo.getIdTituloCnt());		
		
		titulo.setIdSap          (actual.getIdSap());
		titulo.setFechaNotifsap  (actual.getFechaNotifsap());
		titulo.setIdContrato     (actual.getIdContrato());
		titulo.setIdTituloCntorig(actual.getIdTituloCntorig());	
		
		
		logger.debug(titulo.toString());
		logger.debug(actual.toString());
		
		if(titulo.getIdSap() != null)
		{
			logger.debug("idsap");												
			if(TipoCambioTitulo.VENTANA.name().equals(tipoCambio))
			{				
				if(titulo.getFechaNotifsap() != null)
				{
				   logger.debug("ventana");
				   titulo.setAjustoVu(1l);
				   titulo.setVidaUtil(titulosUtilBO.calcularVidaUtil    (titulo.getFechaInicio(),titulo.getFechaFin()));
				   titulo.setMesesvu (titulosUtilBO.calculaMesesVidaUtil(titulo.getFechaInicio(),titulo.getFechaFin()));
				   
					if (actual.getFechaContabilizacion() == null)
						titulo.setFechaContabilizacion(new Date());
				   
				   tituloDmDAO.actualizarTituloCambio(titulo);	
				}	
				else 
					throw new TituloException("No se puede modificar, no se ha notificado a sap.");
			}
			
			if(TipoCambioTitulo.COSTO.name().equals(tipoCambio))
			{
				if(titulo.getFechaNotifsap() != null)
				{			
					logger.debug("costo");
					actual.setPrecioAnual(titulo.getPrecioAnual());
					actual.setPagoCateg(titulo.getPagoCateg());
					actual.setAnioContrato(titulo.getAnioContrato());
					actual.setFechaContabilizacion(titulo.getFechaContabilizacion());
					actual.setFlgcalculoPrecio(titulo.getFlgcalculoPrecio());					
					tituloDmDAO.actualizarTitulo(actual);																			
				}				
				else 
				    throw new TituloException("No se puede modificar, no se ha notificado a sap.");
			}
									
			if(TipoCambioTitulo.TRASLADAR.name().equals(tipoCambio))
			{
				if(titulo.getFechaNotifsap() != null)
				{				   		
				   logger.debug("trasladar");
				   titulo.setVidaUtil       (titulosUtilBO.calcularVidaUtil(titulo.getFechaInicio(),titulo.getFechaFin()));
				   titulo.setMesesvu        (titulosUtilBO.calculaMesesVidaUtil(titulo.getFechaInicio(),titulo.getFechaFin()));
				   titulo.setEstatus        ("ACTIVO");
				   titulo.setIdSapAnt       (actual.getIdSap());
				   titulo.setIdSap          ("PDTE");
				   titulo.setFechaNotifsap  (actual.getFechaNotifsap());
				   titulo.setIdTituloCntant (actual.getIdTituloCnt());				   
				   titulo.setAjustoTitulo(1l);				   
								   				   
				   tituloDmDAO.insertarTitulo(titulo);				   
				   
				   titulo.setIdTituloCntorig(titulo.getIdTituloCnt());				   
				   tituloDmDAO.actualizarTitulo(titulo);
				   
				   actual.setEstatus("ELIMINADO");
				   actual.setIdTituloCntnvo(titulo.getIdTituloCnt());
				   tituloDmDAO.actualizarTitulo(actual);				   				 
				}	
			}	
		}				
	}		
	
	
	/**
	 * @throws TituloException 
	 * @see Se traslada los valores de sapnum al nuevo titulo que se ha trasladado, siempre y cuando el costo sea el mismo
	 */
	public void actualizarSapnumTraslados(Long idContrato) throws TituloException{		
	
		List<Titulo>  titulos = tituloDmDAO.buscarTitulosTrasladados(idContrato, "M");
		
		for(Titulo t : safeList(titulos) )
		{
			Titulo tituloAnterior = tituloDmDAO.buscarTituloEstatus(t.getIdTituloCntant(), "ELIMINADO");
									
			if(tituloAnterior != null)								
			{
				double costoAnt    =  tituloAnterior.getCosto() == null ? 0.0 : Double.parseDouble(tituloAnterior.getCosto());
				double costoActual =  t.getCosto() == null ? 0.0 : Double.parseDouble(t.getCosto());
				
				if(costoActual == costoAnt ) 
				{
					List<SapNum>  sapNumContables  = tituloDmDAO.getSAPNUMTrasladados(t.getIdTituloCntant());
					for(SapNum s: safeList(sapNumContables) )
					{
						s.setIdTituloCNT(t.getIdTituloCnt());
						s.setIdTituloCtoOrig(t.getIdTituloCntorig());
						tituloDmDAO.insertarSapSubnum(s);
					}											
				}					
			}			
		}				
	}
	

	/**
	 * @throws TituloException 
	 * @see Se actualiza la bandera ajusto_valor para los titulos traslados
	 */
	public void actualizarBanderasTraslados(Long idContrato) throws TituloException{		
	
		List<Titulo>  titulos = tituloDmDAO.buscarTitulosTrasladados(idContrato, "M");
		
		for(Titulo t : safeList(titulos) )
		{
			Titulo tituloAnterior = tituloDmDAO.buscarTituloEstatus(t.getIdTituloCntant(), "ELIMINADO");
									
			if(tituloAnterior != null)								
			{
				double costoAnt    =  tituloAnterior.getCosto() == null ? 0.0 : Double.parseDouble(tituloAnterior.getCosto());
				double costoActual =  t.getCosto() == null ? 0.0 : Double.parseDouble(t.getCosto());
				
				if(costoActual == costoAnt ) 
				{					
					t.setAjustoValor(null);
					tituloDmDAO.actualizarTitulo(t);
				}
				else
				{
					t.setAjustoValor(1l);
					tituloDmDAO.actualizarTitulo(t);
				}	
			}			
		}				
	}

	/**
	 * @see Se notifica a Sap actualizacion de los costos de los titulos
	 */
	public void actualizaCostoTodosLosTitulos(ContratoDTO contrato)
	{
		try
		{
			ResponseValorTituloDTO response = commonRestBO.llamadaCostoTitulo(contrato.getFechaInicio(), contrato.getIdContrato(), "ESPEJO");
			if(!response.getResultado())			
			    logger.error("No fue posible actualizar el costo razon:[{}]",response.getMensaje());		
		}
		catch(Exception e)
		{
			logger.error("Ocurrio un error al tratar de generar los costos",e);
		}
	}
			
	public void procesoActualizacionTitulosEnmienda(Long idCto)
	{
		InfoDetalleContratoDTO detalle = generalesDAO.obtenDetalleContrato(idCto);
		if(detalle.getTipoMovimiento().intValue() == Movimiento.Enmienda.getValor().intValue())
		{			
			List<Titulo>  titulos = tituloDmDAO.buscarTitulosEnmiendaSincro(idCto, "T");
			logger.debug("Se inicia el proceso de sincronizacion con enmiendas con titulos:{}",titulos.size());
			for(Titulo t: titulos)
			{
				tituloDmDAO.borrarRegistroSAPNUM(t.getIdTituloCnt());
				actualizarCostoEnmienda(t,"T");
			}				
		}		
	}
	
	public String procesoActualizacionTitulosEnmienda(Long idCto, String prefix)
	{
		String        titulosNoValidos = null;
		InfoDetalleContratoDTO detalle = generalesDAO.obtenDetalleContrato(idCto);
		
		if(detalle.getTipoMovimiento().intValue() == Movimiento.Enmienda.getValor().intValue())
		{			
			List<Titulo>  titulos = tituloDmDAO.buscarTitulosEnmiendaSincro(idCto, prefix);
			logger.debug("Se inicia el proceso de sincronizacion con enmiendas con titulos:{}",titulos.size());
			for(Titulo t: titulos)
			{
				logger.info("id eliminar sap-num {}", t.getIdTituloCnt());
				
				String res = this.verificarCostoEnmienda(t);
				if(res == null)
				{
					tituloDmDAO.borrarRegistroSAPNUM(t.getIdTituloCnt());
					actualizarCostoEnmienda(t,prefix);	
				}
				else
				{
					if(titulosNoValidos == null)
						titulosNoValidos = t.getDescTitulo();
					else
					    titulosNoValidos = titulosNoValidos + "," + t.getDescTitulo();
				}				
			}				
		}	
		return titulosNoValidos;
	}
	
	
	
	/**
	 * @see Se actualizan banderas en bd para cambio en un titulo
	 */
	public void actualizarCostoEnmienda(Titulo titulo, String prefix)
	{	
		logger.info("Actualizacion banderas cambio de titulo-enmienda");
		logger.info(titulo.toString());
		
		Double                diferencia            = this.obtenerDiferenciaCostoTitulo(titulo);	                        //se obtiene la diferencia del costo del titulo	 	     		   
		List<AmortizacionSAP> valoresNetosContables = tituloDmDAO.buscaMaxSubNumByFechaAmort(titulo.getIdTituloCntorig(), this.getAnioActual());//los valores contables registrados por sap
		List<SapNum>          sapNumContables       = tituloDmDAO.getAumnentosSAPNUMContables(titulo.getIdTituloCntorig(), this.getAnioActual());   //son los sabnum registrados en sap_amortizacion		   
		
		logger.info("Diferencia {}", diferencia);   
		if(diferencia.doubleValue() < 0 && titulo.getFechaNotifsap() != null)
		{			   		   			
			SapNum sapNum = new SapNum(titulo.getIdContrato(), titulo.getIdTituloCnt(), titulo.getIdTituloCntorig());
			titulo.setAjustoValor(-1L);
			titulo.setAjustoVu(null);
			sapNum.setDisminucion(df.format(diferencia*-1));
			
			tituloDmDAO.actualizarTituloVar(titulo, prefix);	
						
			//Se verifica si es existen valos netos contables
			//De no haber se hacer un insert en sap_num con 0000 y la diferencia
	   		//Si existen valos netos contables se verifica que: costo del titulo > valor neto contable
			if(valoresNetosContables == null || valoresNetosContables.isEmpty())
			{
				sapNum.setSubnumero("0000");
				tituloDmDAO.insertarSapSubnum(sapNum);
			} 		   			   
			else
			{								
			   //Si asigna la diferencia en algun valor subnum accesible					   														
			   this.asignarDiferenciaSubNum(diferencia, sapNumContables, valoresNetosContables, sapNum);			   
			}			   			
		}
		else if(diferencia.doubleValue() > 0 && titulo.getFechaNotifsap() != null)
		{
			SapNum sapNum = new SapNum(titulo.getIdContrato(), titulo.getIdTituloCnt(), titulo.getIdTituloCntorig());
			titulo.setAjustoValor(1L);				
			titulo.setAjustoVu(null);
			sapNum.setAumento(df.format(diferencia));
					
			tituloDmDAO.actualizarTituloVar(titulo, prefix);						
			tituloDmDAO.insertarSapSubnum(sapNum);
			logger.info("sap-num guardao {}", sapNum.toString());			
		}
		else
		{
			logger.info("Diferencia 0 se actuliza titulo");
			titulo.setAjustoValor(0L);
			tituloDmDAO.actualizarTituloVar(titulo, prefix);
		}
	}
	
	public Double obtenerDiferenciaCostoTitulo(Titulo titulo){
		
		logger.info("Actualizacion banderas cambio de titulo-enmienda");
		logger.info(titulo.toString());		
													
		SapNum  anterior  = tituloDmDAO.getAumentosDisminXTitulo(titulo.getIdTituloCntorig(), titulo.getIdContrato());
		
		if(anterior != null)
		   logger.info("previo {}", anterior.toString());
		
		Double aumentos   = anterior == null || anterior.getAumento()     == null ? 0.0 : Double.parseDouble(anterior.getAumento());
		Double disminucion= anterior == null || anterior.getDisminucion() == null ? 0.0 : Double.parseDouble(anterior.getDisminucion());
		Double costo      = titulo.getCosto() == null ? 0.0 : Double.parseDouble(titulo.getCosto());
		Double diferencia = costo - ( aumentos - disminucion);
		
		diferencia = Math.round(diferencia.doubleValue() * 100.0) / 100.0;
		return diferencia;
	}
	
	public int getAnioActual(){
		 Calendar cal = Calendar.getInstance();
	     cal.setTime(new Date());
	     int year = cal.get(Calendar.YEAR);
	     return year;
	}
		
	public String verificarCostoEnmienda(Titulo titulo)
	{
		logger.info("se inicia validacion enimeinda costo");
		String valido = null;		
		
		if(titulo.getFechaNotifsap() != null)   //Se verifica que el titulo ya este notificado a sap
		{				
		   Double                diferencia            = this.obtenerDiferenciaCostoTitulo(titulo);	                        //se obtiene la diferencia del costo del titulo	 	     		   
		   List<AmortizacionSAP> valoresNetosContables = tituloDmDAO.buscaMaxSubNumByFechaAmort(titulo.getIdTituloCntorig(), this.getAnioActual());//los valores contables registrados por sap
		   List<SapNum>          sapNumContables       = tituloDmDAO.getAumnentosSAPNUMContables(titulo.getIdTituloCntorig(), this.getAnioActual());   //son los sabnum registrados en sap_amortizacion		   
		   
		   if(diferencia.doubleValue() < 0)
		   {			   		   
			   //Se verifica si es existen valos netos contables
			   //De no haber se hacer un insert en sap_num con 0000 y la diferencia
	   		   //Si existen valos netos contables se verifica que: diferencia < valor neto contable
			   if(valoresNetosContables == null || valoresNetosContables.isEmpty()) 		   
				   valido = null;		   
			   else
			   {
				   double valorNetoContable = 0.0;
				   for(AmortizacionSAP s : safeList(valoresNetosContables))
					   valorNetoContable += Double.parseDouble(s.getVnc());
				   
				   logger.debug("vnc:{}", valorNetoContable);
				   logger.debug("diferencia:{}", diferencia);
				   if(Math.abs(diferencia.doubleValue()) <= valorNetoContable)
				   {
					 //Si el valor neto contable es menor al costo del titulo se procede a verificar si existe algun aumento para asignar la difernecia					   
					 boolean asignado = this.verificarAsignarDiferenciaSubNum(diferencia, sapNumContables, valoresNetosContables);
					 if(!asignado)
					    valido = titulo.getDescTitulo();
				   }
				   else
				   {
					   valido = titulo.getDescTitulo();					   					   																											   
				   }   
			   }
		   }
		}									
		logger.info("valido {}", valido);
		return valido; 											
	}
	
	public boolean verificarAsignarDiferenciaSubNum(Double disminucion, List<SapNum> sapNumContables, List<AmortizacionSAP> valoresNetosContables){		 
	     		 	    				 			     		
		 Boolean asignado            = false;		 
		 double disminucionVerificar = disminucion.doubleValue()*-1;
		 
		 for(AmortizacionSAP m : safeList(valoresNetosContables))
		 {		
			 if(disminucionVerificar > 0)
			 {
				 //por cada valor neto contable se obtiene el monto posible a asingar = vnc - aumento (registro en tabla sap_num)
				 double valorNeto     = Double.parseDouble(m.getVnc());				 				 				 				 
				 disminucionVerificar = disminucionVerificar - valorNeto;	 
			 }			 		
			 
			 if(disminucionVerificar <= 0)
			 {
				 asignado = true;
				 break; 
			 } 
		 }
						
		 logger.info("Se ha asignado la disminucion? {}", asignado);		 		 
		 return asignado;
	}
	
	public void asignarDiferenciaSubNum(Double disminucion, List<SapNum> sapNumContables, List<AmortizacionSAP> valoresNetosContables, SapNum sapNum){		 
		 Boolean asignado            = false;		 
		 double disminucionVerificar = disminucion.doubleValue()*-1;
		 
		 for(AmortizacionSAP m : valoresNetosContables)
		 {		
			 if(disminucionVerificar > 0)
			 {
				 //por cada valor neto contable se obtiene el monto posible a asingar = vnc - aumento (registro en tabla sap_num)
				 double valorNeto = Double.parseDouble(m.getVnc());				 				 				 				
				 
				 if(valorNeto > 0.0)
				 {
					 sapNum.setSubnumero(m.getSubNumero());
					 String valor = valorNeto >= disminucionVerificar ? String.valueOf(disminucionVerificar) : String.valueOf(valorNeto); 					 
					 sapNum.setDisminucion(valor);
					 tituloDmDAO.insertarSapSubnum(sapNum);
				 }					 
				 disminucionVerificar = disminucionVerificar - valorNeto;	 
			 }			 		
			 
			 if(disminucionVerificar <= 0)
			 {
				 asignado = true;
				 break; 
			 } 
		 }						
		 logger.info("Se ha asignado la disminucion? {}", asignado);		 		 		 
	}
	
	public ContratoDTO calculaFechasInicioFinValidacion(ContratoDTO contrato)
	{
		ContratoDTO cv=null;
		try 
		{
			cv = (ContratoDTO) BeanUtils.cloneBean(contrato);
			FechasDTO fechas = generalesDAO.obtenerFechasMaximaMinimaDeContratosPorContratoOriginal(contrato.getIdContratoOriginal());
			cv.setFechaInicio(fechas.getInicio());
			cv.setFechaFin(fechas.getFin());
		} catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) 
		{
			logger.error("Error ",e);
		}
		return cv;
	}
	
	@SuppressWarnings("unchecked")
	public static <T> List<T> safeList(final List<T> other) {
		return other == null ? Collections.EMPTY_LIST : other;
	}

}