/**
 * 
 */
package com.ejie.ad74a.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
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.LiquidacionDao;
import com.ejie.ad74a.exception.ResourceNotFoundException;
import com.ejie.ad74a.model.Ayuntamiento;
import com.ejie.ad74a.model.ConfigSipca;
import com.ejie.ad74a.model.FacturacionAyuntamiento;
import com.ejie.ad74a.model.Liquidacion;
import com.ejie.ad74a.model.Solicitante;
import com.ejie.ad74a.service.LiquidacionService;
import com.ejie.ad74a.util.UtilLiquidaciones;
import com.ejie.ad74a.util.UtilProperties;
import com.ejie.ad74a.util.Utilidades;
import com.ejie.ad74a.util.constantes.ConstantesBatch;
import com.ejie.ad74a.util.constantes.ConstantesBatch.LiqValMapKey;
import com.ejie.ad74a.util.mappers.ComunicacionMapper;
import com.ejie.ad74a.util.mappers.LiquidacionMapper;
import com.ejie.ad74a.ws.client.televida.model.Comunicacion;
import com.ejie.ad74a.ws.client.televida.model.RespuestaType;
import com.ejie.ad74a.ws.client.televida.service.LiquidacionWS;

/**
 * Implementacion de la capa de servicio para liquidaciones en AE01B
 * 
 * @author BICUGUAL
 *
 */
@Service
public class LiquidacionServiceImpl implements LiquidacionService {

	private static final String IBAN_CEROS = "ES8200000000000000000000";

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

	@Autowired
	private LiquidacionWS liqWs;

	@Autowired
	private LiquidacionDao liqDao;

	@Override
	public ConfigSipca getConfSipca() {
		try {
			return liqDao.getConfigSipca();
		} catch (EmptyResultDataAccessException e) {
			throw new ResourceNotFoundException();
		}
	}

	@Override
	public int getDiasLimitePago() {
		try {
			return liqDao.getDiasLimPag();
		} catch (EmptyResultDataAccessException e) {
			throw new ResourceNotFoundException();
		}
	}

	/*
	 * Este metodo va en una nueva transaccion para que los registros de la tabla
	 * espejo persistan aunque la transaccion padre falle.
	 */
	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
	public void generateLiquidacionesEspejo(List<Solicitante> lstSolicitantesConMensualidad, boolean relanzamiento,
			String idEjecucion, boolean modoPrueba) {

		List<Liquidacion> lstLiquidaciones = LiquidacionMapper.getInstace(this.getConfSipca(), this.getDiasLimitePago())
				.lstSolicitantesToLiquidaciones(lstSolicitantesConMensualidad, relanzamiento);

		logger.info("Numero de liquidaciones a generar en tabla espejo: {}", lstLiquidaciones.size());

		/*- Borrar mensualidades ya generadas con el mismo idEjecucion (si hubiera), para que no se dupliquen */
		liqDao.deleteLiquidacionesEspejoEjecucion(idEjecucion);
		if (!lstLiquidaciones.isEmpty()) {
			/*
			 * Al ser la creacion en la tabla espejo, no es necesario crear los conceptos de
			 * liquidacion aqui, ya que sus registros son una "derivada de los de la tabla
			 * liquidacion. Esos conceptos se crearan al volcar los registros a la tabla
			 * final.
			 */
			liqDao.createLiquidacionesEspejoBatch(lstLiquidaciones, idEjecucion,
					Integer.valueOf(UtilProperties.Props.BATCH_SIZE.val()), modoPrueba);
		}

	}

	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
	public void volcarLiquidacionesEspejoAFinales(String idEjecucion) {
		int numVolcados = liqDao.volcarLiquidacionesEspejoAFinales(idEjecucion);
		logger.info("{} registros volcados a la tabla final de LIQUIDACIONES", numVolcados);
		// Crear los conceptos ahora si...
		numVolcados = liqDao.createConceptosLiquidacion(idEjecucion);
		logger.info("{} registros de conceptos creados en la tabla de CONCEPTOS_LIQ", numVolcados);
	}

	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.NOT_SUPPORTED, rollbackFor = Throwable.class)
	public Map<LiqValMapKey,Collection<String>> comprobarLiquidacionesGeneradas(String idEjecucion) {

		Map<LiqValMapKey, Collection<String>> result = new HashMap<>();
		//Se inicializa lista de errores y avisos:
		result.put(LiqValMapKey.ERRORS, new ArrayList<String>());
		result.put(LiqValMapKey.WARNINGS, new ArrayList<String>());
		/*
		 * [jbhg] Para no empeorar el rendimiento del proceso, estas validaciones se
		 * realizan directamente contra la base de datos
		 */
		result.get(LiqValMapKey.ERRORS).addAll(liqDao.comprobarLiquidacionesGeneradas(idEjecucion));
		
		/**
		 * [ihg] Validaciones que se han de hacer 1 a 1 al ser comprobaciones que no se puedes hacer directamente en BBDD
		 */
		List<Liquidacion> liquidacionesEspejo = this.liqDao.obtenerLiquidacionesGeneradasEspejo(idEjecucion);
		
		for(Liquidacion liquidacion: liquidacionesEspejo) {
			validaNumIdentificacion(result, liquidacion);
			// comprobar que los numeros de cuenta son correctos (funcion java para validar numeros de cuenta)
			if(StringUtils.isBlank(liquidacion.getIban())) {
				result.get(LiqValMapKey.ERRORS).add(String.format(
						"IBAN no informado para idLiquidacion00=%s (numAbonado=%s)", liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
			}else if(!UtilLiquidaciones.isValidIban(liquidacion.getIban())){
				result.get(LiqValMapKey.ERRORS).add(String.format(
						"IBAN %s no es correcto para idLiquidacion00=%s (numAbonado=%s)", liquidacion.getIban(),liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
			}else if(IBAN_CEROS.equalsIgnoreCase(liquidacion.getIban())) {
				result.get(LiqValMapKey.WARNINGS).add(String.format(
						"IBAN %s de prueba para idLiquidacion00=%s (numAbonado=%s)", liquidacion.getIban(),liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
			}
			
			if(liquidacion.getNomRacSoc() == null || liquidacion.getNomRacSoc().trim().equals("")) {
				result.get(LiqValMapKey.ERRORS).add(String.format(
						"Raz\u00F3n social no informada para idLiquidacion00=%s (numAbonado=%s)",liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
			}
		}
		
		return result;
	}

	//Validacion de numeros de identificacion
	private void validaNumIdentificacion(Map<LiqValMapKey, Collection<String>> result, Liquidacion liquidacion) {
		// comprobar que el tipo tercero es correcto (funcion java para validar identificador y tipo de identificador)
		if(liquidacion.getTipoTercero() == null || liquidacion.getTipoTercero() == 0)  {
			result.get(LiqValMapKey.ERRORS).add(String.format(
					"Tipo tercero no informado para idLiquidacion00=%s (numAbonado=%s)", liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
		}else {
			switch (liquidacion.getTipoTercero()) {
			case ConstantesBatch.CIF:
				if(liquidacion.getTercero() != null && !Utilidades.isValidCif(liquidacion.getTercero())) {
					result.get(LiqValMapKey.WARNINGS).add(String.format("El tercero %s no es un CIF v\u00E1lido para idLiquidacion00=%s (numAbonado=%s)",liquidacion.getTercero(), liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
				}					
				break;
			case ConstantesBatch.DNI_NIE:
				if(liquidacion.getTercero() != null && !Utilidades.isValidNif(liquidacion.getTercero())) {
					result.get(LiqValMapKey.WARNINGS).add(String.format("El tercero %s no es un DNI/NIE v\u00E1lido para idLiquidacion00=%s (numAbonado=%s)",liquidacion.getTercero(), liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
				}
				break;
			case ConstantesBatch.PASAPORTE:
				// De momento no se hacen validaciones de pasaportes
				break;

			default:
				result.get(LiqValMapKey.ERRORS).add(String.format(
						"Tipo tercero no correcto para idLiquidacion00=%s (numAbonado=%s)", liquidacion.getIdLiquidacion(), liquidacion.getExpediente()));
				break;
			}
		}
	}

	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
	public RespuestaType generateLiquidacionAyto(FacturacionAyuntamiento factura, Ayuntamiento ayto,
			boolean relanzamiento) throws Exception {

		Comunicacion comunicacion = ComunicacionMapper.getInstace(this.getDiasLimitePago())
				.facturacionAytoToComunicacion(factura, ayto, relanzamiento);
		return liqWs.emitirLiquidacion(comunicacion);

	}

}
