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

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.result.ResultMapException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.mx.dla.dda.contrato.amortizacion.daos.AmortizacionDAO;
import com.mx.dla.dda.contrato.fees.daos.FeesContratoDAO;
import com.mx.dla.dda.contrato.fees.dtos.FeesContratoPagoBD;
import com.mx.dla.dda.contrato.fees.dtos.FeesContratoPagoDTO;
import com.mx.dla.dda.contrato.fees.dtos.FeesFlatBD;
import com.mx.dla.dda.contrato.fees.dtos.FeesSuscriptorBD;
import com.mx.dla.dda.contrato.generales.bos.GeneralesBO;
import com.mx.dla.dda.contrato.generales.dtos.ContratoDTO;
import com.mx.dla.dda.contrato.titulo.bos.TitulosBussinesBO;
import com.mx.dla.dda.contrato.transaccion.exceptions.dtos.TransaccionException;
import com.mx.dla.global.bos.BaseBO;

@Service
public class FeesContratoBO extends BaseBO {

	@Autowired
	private FeesContratoDAO feesContratoDAO;

	@Autowired
	private AmortizacionDAO amortizacionDAO;

	@Autowired
	private FeesSuscriptorBO feesSuscriptorBO;

	@Autowired
	private FeesFlatBO feesFlatBO;

	@Autowired
	private FeesBonoBO feesBonoBO;

	@Autowired
	private FeesCategoriaBO feesCategoriaBO;

	@Autowired
	private GeneralesBO generalesBO;

	@Autowired
	private TitulosBussinesBO titulosBussinesBO;

	private ContratoDTO cto;

	public List<FeesContratoPagoDTO> getDatosFees(Long idContrato) throws ParseException {
		List<FeesContratoPagoDTO> datos = new ArrayList<FeesContratoPagoDTO>();

		List<FeesContratoPagoBD> periodos = feesContratoDAO.getPeriodos(idContrato); // Se obtienen los periodos que
																						// puede llegar a tener un
																						// contrato

		if (periodos.isEmpty()) // si no existe registro en bd
		{
			FeesContratoPagoDTO dato = feesSuscriptorBO.crearSuscriptor(null, null);
			FeesContratoPagoDTO datoFlat = feesFlatBO.crearFlat(null, null);
			dato.setFlats(datoFlat.getFlats());
			dato.setMaxAnios(datoFlat.getMaxAnios());
			datos.add(dato);
		}

		else { // se sacan los datos y se obtiene datos por periodo.
			List<FeesSuscriptorBD> suscriptores = new ArrayList<FeesSuscriptorBD>();
			List<FeesFlatBD> flats = new ArrayList<FeesFlatBD>();
			List<Long> idsPeriodos = new ArrayList<Long>();

			for (FeesContratoPagoBD item : periodos)
				idsPeriodos.add(item.getIdContratoPago());

			if (periodos.get(0).getTipoPago().compareTo("ESCALA") == 0) {
				suscriptores = feesContratoDAO.getFeesSuscriptor(idsPeriodos);
				for (FeesContratoPagoBD periodo : periodos) {
					List<FeesSuscriptorBD> temporal = new ArrayList<FeesSuscriptorBD>();
					for (FeesSuscriptorBD suscriptor : suscriptores) {
						if (periodo.getIdContratoPago().longValue() == suscriptor.getIdContratoPago().longValue())
							temporal.add(suscriptor);
					}
					datos.add(feesSuscriptorBO.crearSuscriptor(periodo, temporal));
				}
			} else if (periodos.get(0).getTipoPago().compareTo("FLAT") == 0) {
				flats = feesContratoDAO.getFeesFlat(idsPeriodos);
				for (FeesContratoPagoBD periodo : periodos) {
					List<FeesFlatBD> temporal = new ArrayList<FeesFlatBD>();
					for (FeesFlatBD flat : flats) {
						if (periodo.getIdContratoPago().longValue() == flat.getIdContratoPago().longValue())
							temporal.add(flat);
					}
					datos.add(feesFlatBO.crearFlat(periodo, temporal));
				}

			} else if (periodos.get(0).getTipoPago().compareTo("NA") == 0) {
				for (FeesContratoPagoBD periodo : periodos)
					datos.add(this.crearFeesNA(periodo));
			}

		}

		return datos;
	}

	public void guardarPagosFees(List<FeesContratoPagoDTO> periodos, Long idContrato)
			throws ParseException, UnsupportedEncodingException, ResultMapException, SQLException, TransaccionException {

		// try {
		feesCategoriaBO.guardarCategorias(periodos, idContrato);
		for (FeesContratoPagoDTO periodo : periodos) {
			if (periodo.getTipoPago().compareTo("ESCALA") == 0)
				feesSuscriptorBO.guardarSuscriptor(periodo, idContrato);
			else if (periodo.getTipoPago().compareTo("FLAT") == 0)
				feesFlatBO.guardarFlat(periodo, idContrato);
			else if (periodo.getTipoPago().compareTo("NA") == 0)
				this.guardarFeesNA(idContrato);
		}
		/*
		 * } catch (Exception e) { logger.error("Error ", e); throw new Exception(e); }
		 */
	}

	public void editarPagosFees(List<FeesContratoPagoDTO> periodos, Long idContrato) throws ParseException, UnsupportedEncodingException, ResultMapException, SQLException, TransaccionException {
		feesCategoriaBO.editarCategorias(periodos, idContrato);
		logger.info("Se han editado las categorias");

		if (periodos.get(0).getTipoPago().compareTo("ESCALA") == 0) // Se eliminan los datos dinamicos anteriores
			feesSuscriptorBO.eliminarRangosCostoSuscriptor(idContrato);
		else if (periodos.get(0).getTipoPago().compareTo("FLAT") == 0)
			feesFlatBO.eliminarPagosFlat(idContrato);

		for (FeesContratoPagoDTO periodo : periodos) // Se hacen las insercciones de los nuevos datos
		{
			if (periodo.getTipoPago().compareTo("ESCALA") == 0) {
				if (periodo.getIdContratoPago() <= 0)
					feesSuscriptorBO.guardarSuscriptor(periodo, idContrato);
				else
					feesSuscriptorBO.editarSuscriptor(periodo);
			} else if (periodo.getTipoPago().compareTo("FLAT") == 0) {
				if (periodo.getIdContratoPago() <= 0)
					feesFlatBO.guardarFlat(periodo, idContrato);
				else
					feesFlatBO.editarFlat(periodo);
			}
		}
	}

	public void eliminarPagosFees(Long idContrato)
			throws ParseException, ResultMapException, SQLException, TransaccionException {
		String tipo = feesContratoDAO.getTipoFees(idContrato);
		try {
			if (tipo.compareTo("ESCALA") == 0)
				feesSuscriptorBO.eliminarSuscriptores(idContrato);
			else if (tipo.compareTo("FLAT") == 0)
				feesFlatBO.eliminarFlats(idContrato);
			else if (tipo.compareTo("NA") == 0)
				feesContratoDAO.eliminarContratoPagoXCto(idContrato);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
	}

	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = PersistenceException.class)
	public void guardarProcesoFees(String request, String requestBono, Integer inflacion, Long idContrato,
			String operacion, String periodosEliminar, String categoriasEliminar)
			throws ParseException, JsonParseException, JsonMappingException, IOException, ResultMapException, SQLException, TransaccionException {
		ObjectMapper mapper = new ObjectMapper();

		List<FeesContratoPagoDTO> datos = mapper.readValue(request, new TypeReference<List<FeesContratoPagoDTO>>() {
		});
		if (datos != null) {
			if (operacion.compareTo("nuevo") == 0) { // Se guarda la informacion por primera vez
				this.guardarPagosFees(datos, idContrato);
				logger.info("Se guarda informacion por primera vez.");
			}

			else if (operacion.compareTo("resetear") == 0) // Se guarda la informacion cuando se cambia el tipo de fees
			{
				logger.info("Se guarda informacion resetear.");
				this.eliminarPagosFees(idContrato);
				feesCategoriaBO.eliminarCategorias(idContrato);
				this.guardarPagosFees(datos, idContrato);
			}

			else if (operacion.compareTo("limpiar") == 0) // Se guarda la informacion cuando se eliminan todos los
															// periodos
			{
				this.eliminarPagosFees(idContrato);
				feesCategoriaBO.eliminarCategorias(idContrato);
				logger.info("Se guarda informacion limpiar.");
			}

			else if (operacion.compareTo("actualizar") == 0) // Se actualiza la informacion del fees actual
			{
				if (periodosEliminar != null) // Se verifica si eliminos periodos el usuario
				{
					List<FeesContratoPagoDTO> periodosBorrar = mapper.readValue(periodosEliminar,
							new TypeReference<List<FeesContratoPagoDTO>>() {
							});
					for (FeesContratoPagoDTO item : periodosBorrar) {
						if (item.getTipoPago().compareTo("ESCALA") == 0)
							feesSuscriptorBO.eliminarSuscriptor(item);
						else
							feesFlatBO.eliminarFlat(item);
					}
					logger.info("Se eliminan los periodos.");
				}

				if (categoriasEliminar != null) // Se procesa las categorias eliminadas en el portal
				{
					List<FeesContratoPagoDTO> feesEliminar = mapper.readValue(categoriasEliminar,
							new TypeReference<List<FeesContratoPagoDTO>>() {
							});
					for (FeesContratoPagoDTO item : feesEliminar) {
						if (item.getTipoPago().compareTo("0") == 0) // Se verifica si se elimino categoria en suscriptor
							datos = feesSuscriptorBO
									.eliminarCategoriaSuscriptor(item.getCategorias().get(0).getIdCategoria(), datos);

						if (item.getTipoPago().compareTo("1") == 0) // Se verifica si se elimino categoria en flat
							datos = feesCategoriaBO.eliminarCategoriaFlat(item.getCategorias().get(0).getIdCategoria(),
									datos);

						if (item.getTipoPago().compareTo("2") == 0) // Se verifica si se elimino un rango en bonos flats
							feesBonoBO.eliminarRangoBono(item);
					}
					logger.info("Se eliminan las categorias.");
				}

				if (datos.size() > 0) // se actulizan el resto de los datos
				{
					if (datos.get(0).getCategorias() != null || datos.get(0).getFlats() != null) {
						this.editarPagosFees(datos, idContrato);
						logger.info("se editan los datos.");
					}

				} else {
					feesCategoriaBO.eliminarCategorias(idContrato);
					logger.info("se elimian categorias??.");
				}

			}
			amortizacionDAO.updateContratoProceso(idContrato, 1); // Se actualiza la bandera de modificacion para
																	// titulos en amortizacion
		}

		titulosBussinesBO.procesoActualizacionTitulosEnmienda(idContrato);
		feesBonoBO.guardarBono(requestBono, idContrato);
		this.actualizarInflacionCto(inflacion, idContrato);
	}

	public int verificarCategoriasTitulos(String operacion, Long id) {
		int numero = 0;
		if (operacion.compareTo("1") == 0) // verificar elimnacion categoria
			numero = feesCategoriaBO.getCategoriasTitulosXCategoria(id).size();

		else if (operacion.compareTo("2") == 0) // verificar eliminacion periodo
			numero = feesCategoriaBO.getCategoriasTitulosXContrato(id).size();

		else if (operacion.compareTo("3") == 0) // verificar fees completo
			numero = feesCategoriaBO.getCategoriasTitulosXContrato(id).size();
		return numero;
	}

	public String actualizarCostoTitulos(Map<String, Object> paramNumeroContrato) {

		return feesCategoriaBO.actualizarCostoTitulos(paramNumeroContrato);
	}

	public FeesContratoPagoDTO crearFeesNA(FeesContratoPagoBD pagoBD) {
		FeesContratoPagoDTO fee = new FeesContratoPagoDTO();

		if (pagoBD == null)
			fee.setTipoPago("NA");
		else
			fee.setTipoPago(pagoBD.getTipoPago());
		return fee;
	}

	public void guardarFeesNA(Long idContrato) throws ParseException {
		FeesContratoPagoBD contratoPago = new FeesContratoPagoBD();
		contratoPago.setIdContrato(idContrato);
		contratoPago.setTipoPago("NA");
		feesContratoDAO.guardarContratoPago(contratoPago);
	}

	public List<String> getVigenciaContrato(Long idContrato) throws ParseException {
		List<String> fechas = new ArrayList<String>();
		String inicio = feesContratoDAO.fechaInicio(idContrato);
		String fin = feesContratoDAO.fechaFin(idContrato);
		fechas.add(inicio);
		fechas.add(fin);
		return fechas;
	}

	public void actualizarInflacionCto(Integer inflacion, Long idContrato) {
		String inflaCto = inflacion != null ? String.valueOf(inflacion) : "";
		feesContratoDAO.actualizarInflacionCto(inflaCto, idContrato);
	}

	public Long obtenerEdoContrato(Long idContrato) throws ResultMapException, SQLException, TransaccionException {
		this.cto = generalesBO.obtenContrato(idContrato);
		return cto.getIdEstatus();
	}

	public Integer getInflacionCto() {
		Integer inflacion = null;
		if (cto.getInflacion() != null && cto.getInflacion().compareTo("") != 0
				&& Integer.parseInt(cto.getInflacion()) > 0)
			inflacion = Integer.parseInt(cto.getInflacion());
		return inflacion;
	}

	public String getCostoTotal(Long idContrato) {
		return feesContratoDAO.getCostoTotal(idContrato);
	}

}