package com.mx.dla.dda.factura.general.bos;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.result.ResultMapException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;

import com.mx.dla.dda.contrato.transaccion.exceptions.dtos.TransaccionException;
import com.mx.dla.dda.factura.general.daos.FacturacionDAO;
import com.mx.dla.dda.factura.general.dtos.ContratoFacturaDTO;
import com.mx.dla.dda.factura.general.dtos.FacturaGeneralDTO;
import com.mx.dla.dda.factura.general.dtos.MesFacturaDTO;
import com.mx.dla.dda.factura.general.dtos.PagoMesContratoBD;
import com.mx.dla.dda.general.dtos.ResponseDTO;
import com.mx.dla.dda.general.utilerias.ListaUtilerias;
import com.mx.dla.global.bos.BaseBO;

@Component
public class FacturacionBO extends BaseBO {

	@Autowired
	private FacturacionDAO facturacionDAO;

	@Autowired
	private FacturacionWServiceBO wsbo;

	public FacturaGeneralDTO cargaFacturaInfoGeneral(Long idFactura) throws ResultMapException, SQLException, TransaccionException {
		FacturaGeneralDTO facturaGeneral = new FacturaGeneralDTO();

		try {
			if (idFactura != null) {
				facturaGeneral.setIdFactura(idFactura);
				facturaGeneral = facturacionDAO.consultaFacturaGeneral(facturaGeneral);
			}
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
		return facturaGeneral;
	}

	public List<ContratoFacturaDTO> cargaFacturaInfoDetalle(Long idEstudio, Long idFactura, Boolean aprovada,
			String indicadorIva) throws ResultMapException, SQLException, TransaccionException {
		List<ContratoFacturaDTO> contratosDetalle = new ArrayList<ContratoFacturaDTO>();
		List<ContratoFacturaDTO> contratos = facturacionDAO.obtenerContratosEstudio(idEstudio, indicadorIva);

		if (contratos != null && contratos.size() > 0) {
			for (ContratoFacturaDTO contrato : contratos) {
				ContratoFacturaDTO contratoFac = idFactura == null ? this.getContratoMesCostoFacturaNula(contrato, 0L)
						: this.getContratoMesCosto(contrato, idFactura, aprovada);
				if (contratoFac != null)
					contratosDetalle.add(contratoFac);
			}
		}
		return contratosDetalle;
	}

	public ContratoFacturaDTO getContratoMesCostoFacturaNula(ContratoFacturaDTO contrato, Long idFactura) throws ResultMapException, SQLException, TransaccionException {
		double totalCto = 0.0, pgdoCto = 0.0, pendienteCto = 0.0, apagarCto = 0.0;
		List<MesFacturaDTO> costoCto = new ArrayList<>();
		List<PagoMesContratoBD> costosMes = new ArrayList<>();
		List<PagoMesContratoBD> costosPagado = new ArrayList<>();
		
		try {
			costosMes = facturacionDAO.obtenerCostoMesContrato(contrato.getIdContratoOri());
			costosPagado = facturacionDAO.obtenerPagoMesContrato(contrato.getIdContratoOri(),
					idFactura); // Se obtiene los costos pagados x mes del contrato
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
		for (PagoMesContratoBD costoMes : costosMes) {
			MesFacturaDTO detalle = null;

			for (PagoMesContratoBD costoPgd : costosPagado)
				if (compareDates(costoMes.getFecha(), costoPgd.getFecha()))
					detalle = this.getMesPago(costoMes, costoPgd, null);

			if (detalle == null) {
				detalle = this.getMesPago(costoMes, null, null);
				detalle.setNuevo(true);
			}

			boolean sumar = false;
			if (detalle.getTotal().doubleValue() - detalle.getPagado().doubleValue() > 0) {
				costoCto.add(detalle);
				sumar = true;
			}

			if (sumar) {
				totalCto += detalle.getTotal().doubleValue();
				pgdoCto += detalle.getPagado().doubleValue();
				pendienteCto += detalle.getPendiente().doubleValue();
				apagarCto += detalle.getPagar().doubleValue();
			}
		}

		contrato.setPagado(pgdoCto);
		contrato.setTotal(totalCto);
		contrato.setPagar(apagarCto);
		contrato.setPendiente(pendienteCto);
		contrato.setMeses(costoCto);

		if (contrato.getTotal().doubleValue() - contrato.getPagado().doubleValue() == 0)
			contrato = null;
		return contrato;
	}

	public ContratoFacturaDTO getContratoMesCosto(ContratoFacturaDTO contrato, Long idFactura, Boolean aprovada)
			throws ResultMapException, SQLException, TransaccionException {
		double totalCto = 0.0, pgdoCto = 0.0, pendienteCto = 0.0, apagarCto = 0.0;
		List<MesFacturaDTO> costoCto = new ArrayList<>();
		List<PagoMesContratoBD> costosMes = new ArrayList<>();
		List<PagoMesContratoBD> costosPagado = new ArrayList<>();
		List<PagoMesContratoBD> costosFactura = new ArrayList<>();

		try {
			costosMes = facturacionDAO.obtenerCostoMesContrato(contrato.getIdContratoOri());
			costosPagado = facturacionDAO.obtenerPagoMesContrato(contrato.getIdContratoOri(), idFactura);
			costosFactura = facturacionDAO.obtenerPagoMesFacturaCto(contrato.getIdContratoOri(), idFactura);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}

		for (PagoMesContratoBD costoMes : costosMes) {
			MesFacturaDTO detalle = null;

			for (PagoMesContratoBD costoFactura : ListaUtilerias.safeList(costosFactura)) {
				if (compareDates(costoMes.getFecha(), costoFactura.getFecha())) {
					detalle = this.getMesPago(costoMes, getMesXFecha(costoMes.getFecha(), costosPagado), costoFactura);
					break;
				}
			}

			if (detalle == null) {
				for (PagoMesContratoBD costoPgd : ListaUtilerias.safeList(costosPagado)) {
					if (compareDates(costoMes.getFecha(), costoPgd.getFecha())) {
						detalle = this.getMesPago(costoMes, costoPgd, getMesXFecha(costoMes.getFecha(), costosFactura));
						break;
					}
				}
			}

			if (detalle == null) {
				detalle = this.getMesPago(costoMes, null, null);
				detalle.setNuevo(true);
			}

			totalCto += detalle.getTotal().doubleValue();
			pgdoCto += detalle.getPagado().doubleValue();
			pendienteCto += detalle.getPendiente().doubleValue();
			apagarCto += detalle.getPagar().doubleValue();
			costoCto.add(detalle);
		}

		contrato.setPagado(pgdoCto);
		contrato.setTotal(totalCto);
		contrato.setPagar(apagarCto);
		contrato.setPendiente(pendienteCto);
		contrato.setMeses(costoCto);

		if (contrato.getPagar() == 0 && contrato.getTotal() == 0) {
			logger.debug(contrato.toString());
			contrato = null;
		}

		return contrato;
	}

	public PagoMesContratoBD getMesXFecha(Date fecha, List<PagoMesContratoBD> costosFactura) {
		PagoMesContratoBD mes = null;
		for (PagoMesContratoBD costoPgd : costosFactura)
			if (compareDates(costoPgd.getFecha(), fecha))
				mes = costoPgd;
		return mes;
	}

	public MesFacturaDTO getMesPago(PagoMesContratoBD costo, PagoMesContratoBD pagado,
			PagoMesContratoBD pagoFacturaMes) {
		double totalMes = 0.0, pgdoMes = 0.0, pendienteMes = 0.0, apagarMes = 0.0;

		totalMes = costo.getMesTotal().doubleValue();
		pgdoMes = pagado != null && pagado.getMesTotal() != null ? pagado.getMesTotal().doubleValue() : 0.0;
		apagarMes = pagoFacturaMes != null && pagoFacturaMes.getMesTotal() != null ? pagoFacturaMes.getMesTotal() : 0.0;

		pendienteMes = totalMes - (pgdoMes + apagarMes);

		MesFacturaDTO detalle = new MesFacturaDTO(null, pgdoMes, apagarMes, pendienteMes, totalMes);

		boolean nuevo = (pagoFacturaMes != null && pagoFacturaMes.getMesTotal() != null) ? false : true;

		detalle.setNuevo(nuevo);
		detalle.setMes(this.getFecha(costo.getFecha()));
		detalle.setIdFactura(costo.getIdFactura());
		return detalle;
	}

	public void guardarFactura(FacturaGeneralDTO factura) throws ResultMapException, SQLException, TransaccionException {
		if (factura.getIdFactura() == null)
			facturacionDAO.creaFacturaGeneral(factura);
		else
			facturacionDAO.actualizaFacturaGeneral(factura);
		this.guardarFacturaDetalle(factura.getContratos(), factura.getIdFactura());

		logger.debug(factura.toString());
	}

	public void guardarFacturaDetalle(List<ContratoFacturaDTO> contratos, Long idFactura) throws ResultMapException, SQLException, TransaccionException {

		try {
			for (ContratoFacturaDTO contrato : contratos) {
				for (MesFacturaDTO mes : ListaUtilerias.safeList(contrato.getMeses())) {
					mes.setMes(getFecha(mes.getMes()));
					if (mes.getNuevo() != null && mes.getNuevo() && mes.getPagar() > 0) {
						logger.debug(idFactura + ":" + contrato.getIdContratoOri() + ":" + mes);
						facturacionDAO.guardarFacturaDetalle(idFactura, contrato.getIdContratoOri(), mes);
					} else if (mes.getNuevo() != null && !mes.getNuevo()) {
						logger.debug(idFactura + ":" + contrato.getIdContratoOri() + ":" + mes);
						facturacionDAO.actualizarFacturaDetalle(idFactura, contrato.getIdContratoOri(), mes);
					}
				}
			}
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
	}

	public void notificarServicioRest(FacturaGeneralDTO factura) {
		logger.debug(factura.toString());

		ResponseDTO respuesta = null;
		if (factura != null)
			respuesta = wsbo.notificaFactura(factura);

		logger.debug("Respuesta servicio: {}", respuesta);

	}

	public FacturaGeneralDTO consultaFactura(Long idFactura) throws ResultMapException, SQLException, TransaccionException {
		FacturaGeneralDTO factura = new FacturaGeneralDTO();
		factura.setIdFactura(idFactura);

		try {
			return facturacionDAO.consultaFacturaGeneral(factura);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
	}

	public String getFecha(Date fecha) {
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
		return sdf.format(fecha);
	}

	public String getFecha(String fecha) {
		String[] meses = fecha.split("/");
		return meses[1] + "-" + meses[0] + "-" + meses[2];
	}

	public boolean compareDates(Date uno, Date dos) {
		Calendar cal1 = Calendar.getInstance();
		cal1.setTime(uno);

		Calendar cal2 = Calendar.getInstance();
		cal2.setTime(dos);

		if (cal1.equals(cal2))
			return true;
		else
			return false;
	}
}