package com.rjconsultores.ventaboletos.dao.hibernate;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.sql.DataSource;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.rjconsultores.ventaboletos.constantes.TipoEventoExtra;
import com.rjconsultores.ventaboletos.dao.ReceitaDespesaDAO;
import com.rjconsultores.ventaboletos.exception.BusinessException;
import com.rjconsultores.ventaboletos.relatorios.receitaDespesa.RelatorioFinanceiroReceitasDespesasBean;
import com.rjconsultores.ventaboletos.utilerias.MoneyHelper;
import com.rjconsultores.ventaboletos.web.utilerias.NamedParameterStatement;

@Repository("receitaDespesaDAO")
public class ReceitaDespesaHibernateDAO implements ReceitaDespesaDAO {
	
	private static final Logger log = LogManager.getLogger(ReceitaDespesaHibernateDAO.class);
	
	@Autowired
	private DataSource dataSourceRead;
	
	private Map<String, Object> parametrosFiltro;

	@Override
	public List<RelatorioFinanceiroReceitasDespesasBean> getRelatorioDetalhado(Map<String, Object> parametros) throws BusinessException {
		Connection con = null;
		try {
			con = dataSourceRead.getConnection();

			StringBuilder sql = new StringBuilder();
			sql.append(" SELECT ")
			    .append("LISTAGG(fp.cvepago||' /'||CAST(cfp.IMPORTE AS VARCHAR(10)),';') WITHIN GROUP( ORDER BY 1 DESC ) AS descpago,")
			    .append(" C.FECHORVTA, P.PUNTOVENTA_ID, P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA, TE.TIPOEVENTOEXTRA_ID, TE.DESCTIPOEVENTO, ")
				.append(" C.NUMSERIEPREIMPRESA, C.NUMFOLIOPREIMPRESO, E.NUMDOCUMENTO,E.EVENTOEXTRA_ID, TE.CVETIPOEVENTO, E.BOLETO_ID, E.DESCINFO  ")
			    .append(" FROM CAJA_DIVERSOS C ")
			    .append(" JOIN EVENTO_EXTRA E ON E.EVENTOEXTRA_ID = C.EVENTOEXTRA_ID ")
			    .append(" JOIN TIPO_EVENTO_EXTRA TE ON TE.TIPOEVENTOEXTRA_ID = E.TIPOEVENTOEXTRA_ID ")
			    .append(" JOIN PUNTO_VENTA P ON P.PUNTOVENTA_ID = C.PUNTOVENTA_ID ")	  
                .append(" JOIN CAJA_DIVERSOS_PAGO cfp ON ( c.cajadiversos_id = cfp.cajadiversos_id)")
			    .append(" JOIN FORMA_PAGO fp ON ( cfp.formapago_id = fp.formapago_id )")
			    .append(" LEFT JOIN BOLETO B ON B.BOLETO_ID = E.BOLETO_ID ")
			    .append("WHERE C.ACTIVO = 1 ");	  
			
			adicionarFiltros(sql, parametros);
			
			sql.append(" GROUP BY ")
			    .append("c.fechorvta, ")
			    .append("p.puntoventa_id, ")
			    .append("p.nombpuntoventa, ")
			    .append("p.numpuntoventa, ")
			    .append("te.tipoeventoextra_id, ")
			    .append("te.desctipoevento, ")
			    .append("c.numseriepreimpresa, ")
			    .append("c.numfoliopreimpreso, ")
			    .append("e.numdocumento, ")
			    .append("e.eventoextra_id, ")
				.append("te.cvetipoevento, ")
				.append("e.boleto_id, ")
				.append("e.descinfo ");
			
			sql.append("ORDER BY C.FECHORVTA, TE.DESCTIPOEVENTO, P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA");
			
			NamedParameterStatement stmt = new NamedParameterStatement(con, sql.toString());
			setParametros(stmt);
			
			HashSet<String> tiposEventosSubstituir = new HashSet<String>();
			tiposEventosSubstituir.add(TipoEventoExtra.TARIFA_SAFER.toString());
			
			ResultSet resultSet = stmt.executeQuery();
			List<RelatorioFinanceiroReceitasDespesasBean> lsBean = new ArrayList<RelatorioFinanceiroReceitasDespesasBean>();
			while (resultSet.next()) {
				RelatorioFinanceiroReceitasDespesasBean bean = new RelatorioFinanceiroReceitasDespesasBean();
				bean.setDescTipoEvento(resultSet.getString("DESCTIPOEVENTO"));
				bean.setFecHorVta(resultSet.getDate("FECHORVTA"));
				bean.setTipoEventoExtraId(resultSet.getString("TIPOEVENTOEXTRA_ID"));
				bean.setNombPuntoVenta(resultSet.getString("NOMBPUNTOVENTA"));
				bean.setNumPuntoVenta(resultSet.getString("NUMPUNTOVENTA"));
				bean.setPuntoVentaId(resultSet.getInt("PUNTOVENTA_ID"));
				bean.setFormaPagamento(resultSet.getString("DESCPAGO"));
				bean.setNumSeriePreImpresa(resultSet.getString("NUMSERIEPREIMPRESA"));
				bean.setNumFolioPreImpreso(resultSet.getString("NUMFOLIOPREIMPRESO"));
				bean.setCveTipoEvento(resultSet.getString("CVETIPOEVENTO"));
				bean.setDescricao(resultSet.getString("DESCINFO"));
				
				if (bean.getCveTipoEvento() != null && tiposEventosSubstituir.contains(bean.getCveTipoEvento())) {
					bean.setNumDocumento(resultSet.getString("BOLETO_ID"));
				} else {
					bean.setNumDocumento(resultSet.getString("NUMDOCUMENTO"));
				}
				
				setFormasDePagamento(bean);
				setValorFormaPagamento(bean);
				
				bean.setIndtipo(MoneyHelper.isMaiorIgual(bean.getPrecio(), BigDecimal.ZERO) ? 1 : 0);
				lsBean.add(bean);
			}
			
			return lsBean;
			
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new BusinessException(e.getMessage(), e);
		}finally {
			try {
				if (con != null && !con.isClosed()) {
					con.close();
				}
			}
			catch (SQLException e) {
				log.error(e.getMessage(), e);
			}
		}
	}

	@Override
	public List<RelatorioFinanceiroReceitasDespesasBean> getRelatorioSintetico(Map<String, Object> parametros) throws BusinessException {
		Connection con = null;
		try {
			con = dataSourceRead.getConnection();

			StringBuilder sql = new StringBuilder();
			sql.append("SELECT P.PUNTOVENTA_ID, P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA, CDP.IMPORTE ")
			   .append("FROM CAJA_DIVERSOS C ")
			   .append("JOIN EVENTO_EXTRA E ON E.EVENTOEXTRA_ID = C.EVENTOEXTRA_ID ")
			   .append("JOIN TIPO_EVENTO_EXTRA TE ON TE.TIPOEVENTOEXTRA_ID = E.TIPOEVENTOEXTRA_ID ")
			   .append("JOIN PUNTO_VENTA P ON P.PUNTOVENTA_ID = C.PUNTOVENTA_ID ")
			   .append("JOIN CAJA_DIVERSOS_PAGO CDP ON CDP.CAJADIVERSOS_ID = C.CAJADIVERSOS_ID ")
			   .append("LEFT JOIN BOLETO B ON B.BOLETO_ID = E.BOLETO_ID ")
			   .append("WHERE C.ACTIVO = 1 ");
			
			adicionarFiltros(sql, parametros);
			
			sql.append("ORDER BY P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA");
			
			NamedParameterStatement stmt = new NamedParameterStatement(con, sql.toString());
			setParametros(stmt);
			
			ResultSet resultSet = stmt.executeQuery();
			Map<Integer, RelatorioFinanceiroReceitasDespesasBean> map = new HashMap<Integer, RelatorioFinanceiroReceitasDespesasBean>();
			while (resultSet.next()) {
				RelatorioFinanceiroReceitasDespesasBean bean = new RelatorioFinanceiroReceitasDespesasBean();
				bean.setPuntoVentaId(resultSet.getInt("PUNTOVENTA_ID"));
				bean.setNombPuntoVenta(resultSet.getString("NOMBPUNTOVENTA"));
				bean.setNumPuntoVenta(resultSet.getString("NUMPUNTOVENTA"));
				
				if(map.containsKey(bean.getPuntoVentaId())) {
					bean = map.get(bean.getPuntoVentaId());
				}
				
				BigDecimal precio = resultSet.getBigDecimal("IMPORTE");
				if(MoneyHelper.isMaiorIgual(precio, BigDecimal.ZERO)) {
					bean.setReceita(MoneyHelper.somar(bean.getReceita(), precio));
				} else {
					bean.setDespesa(MoneyHelper.somar(bean.getDespesa(), precio));
				}
				
				map.put(bean.getPuntoVentaId(), bean);
			}
			List<RelatorioFinanceiroReceitasDespesasBean> lsBean = new ArrayList<RelatorioFinanceiroReceitasDespesasBean>(map.values());
			
			return lsBean;
			
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new BusinessException(e.getMessage(), e);
		}finally {
			try {
				if (con != null && !con.isClosed()) {
					con.close();
				}
			}
			catch (SQLException e) {
				log.error(e.getMessage(), e);
			}
		}
	}

	@Override
	public List<RelatorioFinanceiroReceitasDespesasBean> getRelatorioAgencia(Map<String, Object> parametros) throws BusinessException {
		Connection con = null;
		try {
			con = dataSourceRead.getConnection();

			StringBuilder sql = new StringBuilder();
			sql.append("SELECT P.PUNTOVENTA_ID, P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA, TE.TIPOEVENTOEXTRA_ID, TE.DESCTIPOEVENTO, ")
			   .append("SUM(CDP.IMPORTE) PRECIO ")
			   .append("FROM CAJA_DIVERSOS C ")
			   .append("JOIN EVENTO_EXTRA E ON E.EVENTOEXTRA_ID = C.EVENTOEXTRA_ID ")
			   .append("JOIN TIPO_EVENTO_EXTRA TE ON TE.TIPOEVENTOEXTRA_ID = E.TIPOEVENTOEXTRA_ID ")
			   .append("JOIN PUNTO_VENTA P ON P.PUNTOVENTA_ID = C.PUNTOVENTA_ID ")
			   .append("JOIN CAJA_DIVERSOS_PAGO CDP ON CDP.CAJADIVERSOS_ID = C.CAJADIVERSOS_ID ")
			   .append("LEFT JOIN BOLETO B ON B.BOLETO_ID = E.BOLETO_ID ")
			   .append("WHERE C.ACTIVO = 1 ");
			
			adicionarFiltros(sql, parametros);
			
			sql.append("GROUP BY P.PUNTOVENTA_ID, P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA, TE.TIPOEVENTOEXTRA_ID, TE.DESCTIPOEVENTO ")
			   .append("ORDER BY P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA, TE.DESCTIPOEVENTO");
			
			NamedParameterStatement stmt = new NamedParameterStatement(con, sql.toString());
			setParametros(stmt);
			
			ResultSet resultSet = stmt.executeQuery();
			List<RelatorioFinanceiroReceitasDespesasBean> lsBean = new ArrayList<RelatorioFinanceiroReceitasDespesasBean>();
			while (resultSet.next()) {
				RelatorioFinanceiroReceitasDespesasBean bean = new RelatorioFinanceiroReceitasDespesasBean();
				bean.setPuntoVentaId(resultSet.getInt("PUNTOVENTA_ID"));
				bean.setNombPuntoVenta(resultSet.getString("NOMBPUNTOVENTA"));
				bean.setNumPuntoVenta(resultSet.getString("NUMPUNTOVENTA"));
				bean.setPrecio(resultSet.getBigDecimal("PRECIO"));
				bean.setIndtipo(MoneyHelper.isMaiorIgual(bean.getPrecio(), BigDecimal.ZERO) ? 1 : 0);
				bean.setTipoEventoExtraId(resultSet.getString("TIPOEVENTOEXTRA_ID"));
				bean.setDescTipoEvento(resultSet.getString("DESCTIPOEVENTO"));
				lsBean.add(bean);
			}
			
			return lsBean;
			
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new BusinessException(e.getMessage(), e);
		}finally {
			try {
				if (con != null && !con.isClosed()) {
					con.close();
				}
			}
			catch (SQLException e) {
				log.error(e.getMessage(), e);
			}
		}
	}

	@Override
	public List<RelatorioFinanceiroReceitasDespesasBean> getRelatorioEvento(Map<String, Object> parametros) throws BusinessException {
		Connection con = null;
		try {
			con = dataSourceRead.getConnection();

			StringBuilder sql = new StringBuilder();
			sql.append("SELECT P.PUNTOVENTA_ID, P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA, TE.TIPOEVENTOEXTRA_ID, TE.DESCTIPOEVENTO, ")
			   .append("SUM(CDP.IMPORTE) PRECIO  ")
			   .append("FROM CAJA_DIVERSOS C ")
			   .append("JOIN EVENTO_EXTRA E ON E.EVENTOEXTRA_ID = C.EVENTOEXTRA_ID ")
			   .append("JOIN TIPO_EVENTO_EXTRA TE ON TE.TIPOEVENTOEXTRA_ID = E.TIPOEVENTOEXTRA_ID ")
			   .append("JOIN PUNTO_VENTA P ON P.PUNTOVENTA_ID = C.PUNTOVENTA_ID ")
			   .append("JOIN CAJA_DIVERSOS_PAGO CDP ON CDP.CAJADIVERSOS_ID = C.CAJADIVERSOS_ID ")
			   .append("LEFT JOIN BOLETO B ON B.BOLETO_ID = E.BOLETO_ID ")
			   .append("WHERE C.ACTIVO = 1 ");
			
			adicionarFiltros(sql, parametros);
			
			sql.append("GROUP BY P.PUNTOVENTA_ID, P.NOMBPUNTOVENTA, P.NUMPUNTOVENTA, TE.TIPOEVENTOEXTRA_ID, TE.DESCTIPOEVENTO ")
			   .append("ORDER BY TE.TIPOEVENTOEXTRA_ID, TE.DESCTIPOEVENTO, P.NOMBPUNTOVENTA");
			
			NamedParameterStatement stmt = new NamedParameterStatement(con, sql.toString());
			setParametros(stmt);
			
			ResultSet resultSet = stmt.executeQuery();
			List<RelatorioFinanceiroReceitasDespesasBean> lsBean = new ArrayList<RelatorioFinanceiroReceitasDespesasBean>();
			while (resultSet.next()) {
				RelatorioFinanceiroReceitasDespesasBean bean = new RelatorioFinanceiroReceitasDespesasBean();
				bean.setPuntoVentaId(resultSet.getInt("PUNTOVENTA_ID"));
				bean.setNombPuntoVenta(resultSet.getString("NOMBPUNTOVENTA"));
				bean.setNumPuntoVenta(resultSet.getString("NUMPUNTOVENTA"));
				bean.setPrecio(resultSet.getBigDecimal("PRECIO"));
				bean.setIndtipo(MoneyHelper.isMaiorIgual(bean.getPrecio(), BigDecimal.ZERO) ? 1 : 0);
				bean.setTipoEventoExtraId(resultSet.getString("TIPOEVENTOEXTRA_ID"));
				bean.setDescTipoEvento(resultSet.getString("DESCTIPOEVENTO"));
				lsBean.add(bean);
			}
			
			return lsBean;
			
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new BusinessException(e.getMessage(), e);
		}finally {
			try {
				if (con != null && !con.isClosed()) {
					con.close();
				}
			}
			catch (SQLException e) {
				log.error(e.getMessage(), e);
			}
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void adicionarFiltros(StringBuilder sql, Map<String, Object> parametros) {
		parametrosFiltro = new HashMap<String, Object>();
		sql.append(" AND (E.BOLETO_ID IS NULL OR E.BOLETO_ID = B.BOLETO_ID) ");
		
		if(parametros.containsKey("empresaId")) {
			sql.append("AND E.EMPRESA_ID = :empresaId ");
			parametrosFiltro.put("empresaId", parametros.get("empresaId"));
		}
		
		boolean isDataFechamentoCaixa = parametros.get("isDataFechamentoCaixa") != null ? BooleanUtils.toBoolean(parametros.get("isDataFechamentoCaixa").toString()) : false;
		
		if(isDataFechamentoCaixa) {
			if(parametros.containsKey("fecInicio")) {
				sql.append("AND C.FECCORTE >= TO_DATE(:fecInicio,'DD/MM/YYYY HH24:MI:SS') ");
				parametrosFiltro.put("fecInicio", parametros.get("fecInicio"));
			}
			
			if(parametros.containsKey("fecFinal")) {
				sql.append("AND C.FECCORTE <= TO_DATE(:fecFinal,'DD/MM/YYYY HH24:MI:SS') ");
				parametrosFiltro.put("fecFinal", parametros.get("fecFinal"));
			}
		} else {
			if(parametros.containsKey("fecInicio")) {
				sql.append("AND C.FECHORVTA >= TO_DATE(:fecInicio,'DD/MM/YYYY HH24:MI:SS') ");
				parametrosFiltro.put("fecInicio", parametros.get("fecInicio"));
			}
			
			if(parametros.containsKey("fecFinal")) {
				sql.append("AND C.FECHORVTA <= TO_DATE(:fecFinal,'DD/MM/YYYY HH24:MI:SS') ");
				parametrosFiltro.put("fecFinal", parametros.get("fecFinal"));
			}
		}
		
		if(parametros.containsKey("numDocumento")) {
			sql.append("AND E.NUMDOCUMENTO = :numDocumento ");
			parametrosFiltro.put("numDocumento", parametros.get("numDocumento"));
		}
		
		if(parametros.containsKey("indTipo")) {
			sql.append("AND TE.INDTIPO = :indTipo ");
			parametrosFiltro.put("indTipo", parametros.get("indTipo"));
		}

		if(parametros.containsKey("lsPuntoVenta")) {
			List<Object> values = parametros.get("lsPuntoVenta") instanceof Collection ? new ArrayList<Object>((Collection) parametros.get("lsPuntoVenta")) : new ArrayList<Object>(((List) parametros.get("lsPuntoVenta")));
			int size = values.size();
			Integer[] ls = new Integer[size];
			for (int i = 0; i < size; i++) {
				ls[i] = Integer.valueOf((values.get(i).toString()));
			}
			sql.append("AND C.PUNTOVENTA_ID IN (" + StringUtils.join(ls, ',') + ") ");
		}
		
		if(parametros.containsKey("lsEventosExtra")) {
			List<Object> values = parametros.get("lsEventosExtra") instanceof Collection ? new ArrayList<Object>((Collection) parametros.get("lsEventosExtra")) : new ArrayList<Object>(((List) parametros.get("lsEventosExtra")));
			int size = values.size();
			Integer[] ls = new Integer[size];
			for (int i = 0; i < size; i++) {
				ls[i] = Integer.valueOf((values.get(i).toString()));
			}
			sql.append("AND TE.TIPOEVENTOEXTRA_ID IN (" + StringUtils.join(ls, ',') + ") ");
		}
	}
	
	private void setParametros(NamedParameterStatement stmt) throws SQLException {
		for (Entry<String, Object> parametro : parametrosFiltro.entrySet()) {
			stmt.setObject(parametro.getKey(), parametro.getValue());
		}
	}
	
	private void setFormasDePagamento(RelatorioFinanceiroReceitasDespesasBean bean) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		String formasPagamento = bean.getFormaPagamento();
		if (formasPagamento != null) {

			final String[] formaPagamento = formasPagamento.split("\\;") ;
			if (formaPagamento.length > 1) {
				int contador = 0;				
				while (contador < formaPagamento.length) {
					final Class cls = bean.getClass();
					final String metodo = "setFormaPagamento" + (contador+1);
					Method 	meth = cls.getMethod(metodo, String.class);
					meth.invoke(bean, formatarFormaDePagamento(formaPagamento[contador]));
					contador++;
				}
			} else {
				bean.setFormaPagamento1(formatarFormaDePagamento(formaPagamento[0]));
			}
		}
	}
	
	
	private void setValorFormaPagamento(RelatorioFinanceiroReceitasDespesasBean bean) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		String formasPagamento = bean.getFormaPagamento();
		if (formasPagamento != null) {

			final String[] formaPagamento = formasPagamento.split("\\;") ;
			if (formaPagamento.length > 1) {
				int contador = 0;				
				while (contador < formaPagamento.length) {
					final Class cls = bean.getClass();
					final String metodo = "setValorFormaPagamento" + (contador+1);
					Method 	meth = cls.getMethod(metodo, BigDecimal.class);
					meth.invoke(bean, formatarValorFormaDePagamento(formaPagamento[contador]));
					contador++;
				}
			} else {
				bean.setValorFormaPagamento1(formatarValorFormaDePagamento(formaPagamento[0]));
			}
		}
		
		bean.setPrecio(MoneyHelper.somar(bean.getValorFormaPagamento1(), bean.getValorFormaPagamento2(), bean.getValorFormaPagamento3()));
	}
	
	private String formatarFormaDePagamento(String formaDePagamento){
	    if(formaDePagamento!=null){
		final String[] formaPagamentoAuxilar = formaDePagamento.split("\\/") ;	
	    return formaPagamentoAuxilar[0];	
	    }
		return null;	
	}
	
	private BigDecimal formatarValorFormaDePagamento(String formaDePagamento){
	    if(formaDePagamento!=null){
		final String[] formaPagamentoAuxilar = formaDePagamento.split("\\/") ;
	    return formaPagamentoAuxilar.length>1? new BigDecimal(formaPagamentoAuxilar[1].replace(",", ".").trim()):new BigDecimal("0.0");	
	    }
		return null;	
	}
}
