package com.rjconsultores.ventaboletos.relatorios.impl;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.math.NumberUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.rjconsultores.ventaboletos.relatorios.utilitarios.Relatorio;
import com.rjconsultores.ventaboletos.relatorios.utilitarios.RelatorioAgenciaFechamentoAntigoBean;
import com.rjconsultores.ventaboletos.web.utilerias.NamedParameterStatement;

import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

public abstract class RelatorioAgenciaFechamento extends Relatorio {

	private static Logger log = LogManager.getLogger(RelatorioAgenciaFechamento.class);
	
	protected List<RelatorioAgenciaFechamentoAntigoBean> lsDadosRelatorio;

	protected TipoRelatorioFechamentoEnum tipoRelatorio;

	public RelatorioAgenciaFechamento(Map<String, Object> parametros, Connection conexao) throws Exception {
		super(parametros, conexao);
	}

	@Override
	protected void processaParametros() throws Exception {
		String fecInicio = parametros.get("fecInicio").toString() + " 00:00:00";
		String fecFinal = parametros.get("fecFinal").toString() + " 23:59:59";
		String empresa = parametros.get("empresa") != null ? parametros.get("empresa").toString() : "";
		String empresaNome = parametros.get("empresaNome") != null ? parametros.get("empresaNome").toString() : "";
		String puntoVenta = parametros.get("NUMPUNTOVENTA") != null ? parametros.get("NUMPUNTOVENTA").toString() : null;
		parametros.put("tipoRelatorio", tipoRelatorio.name());
		lsDadosRelatorio = new ArrayList<RelatorioAgenciaFechamentoAntigoBean>();

		carregarDadosTotaisVenda(fecInicio, fecFinal, empresa, empresaNome, puntoVenta);
		setLsDadosRelatorio(lsDadosRelatorio);
	}

	public abstract StringBuilder carregarDados(String puntoVenta, String empresa);

	protected String criarQueryBilhetesVenda(String puntoVenta, String empresa) {
		StringBuilder sql = this.carregarDados(puntoVenta, empresa);
		return filtrosPadraoQueryRelatorioFechamento(puntoVenta, empresa, sql);

	}

	protected abstract String criarQueryReceitaDespesa(String puntoVenta, String empresa);

	/**
	 * @param puntoVenta
	 * @param empresa
	 * @param sql
	 */
	protected String filtrosPadraoQueryRelatorioFechamento(String puntoVenta, String empresa, StringBuilder sql) {
		sql.append(" FROM CAJA c ");
		sql.append(" INNER JOIN PUNTO_VENTA pv ");
		sql.append(" ON c.PUNTOVENTA_ID = pv.PUNTOVENTA_ID ");
		sql.append(" LEFT JOIN PUNTO_VENTA PV_GERENCIA ON PV_GERENCIA.PUNTOVENTA_ID = PV.PUNTOVENTAPADRE_ID ");
		sql.append(" INNER JOIN marca m ");
		sql.append(" ON m.marca_id     = c.marca_id ");
		sql.append(" WHERE c.TURNO_ID IS NOT NULL ");
		sql.append(" AND c.ACTIVO = 1 ");
		sql.append(" AND c.FECCORTE BETWEEN :de AND :ate ");
		if (puntoVenta != null && !puntoVenta.equals("-1")) {
			sql.append("        AND c.PUNTOVENTA_ID in (" + puntoVenta + ") ");
		}
		sql.append(" AND c.TIPOVENTA_ID  <> 6 ");
		sql.append(" AND c.INDREIMPRESION = 0 ");
		if (empresa != null && !empresa.equals("") && !empresa.equals("-1")) {
			sql.append(" AND m.empresa_id     = " + empresa + " ");
		}
		criarGroupByEOrderBy(sql);

		return sql.toString();
	}

	/**
	 * @param sql
	 */
	protected abstract void criarGroupByEOrderBy(StringBuilder sql);

	/**
	 * @param fecInicio
	 * @param fecFinal
	 * @param empresa
	 * @param empresaNome
	 * @param puntoVenta
	 * @throws SQLException
	 * @throws ParseException
	 */
	protected void carregarDadosTotaisVenda(String fecInicio, String fecFinal, String empresa, String empresaNome, String puntoVenta) throws SQLException, ParseException {
		boolean isDiario = TipoRelatorioFechamentoEnum.DIARIO.equals(tipoRelatorio);
		NamedParameterStatement stmt = new NamedParameterStatement(getConexao(), criarQueryBilhetesVenda(puntoVenta, empresa));
		
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
		ResultSet rset1 = criaResultSet(fecInicio, fecFinal, sdf, stmt);
		String dataInicial = fecInicio;
		String dataFinal = fecFinal;
		sdf =  (isDiario ? new SimpleDateFormat("yyyy-MM-dd hh:mm:ss") : new SimpleDateFormat("dd/MM/yyyy hh:mm:ss"));
		StringBuilder puntoVentaIds = new StringBuilder();
		puntoVentaIds.append(" 0");

		while (rset1.next()) {

			RelatorioAgenciaFechamentoAntigoBean agenciaFechamento = new RelatorioAgenciaFechamentoAntigoBean();
			BigDecimal total = BigDecimal.ZERO;
			BigDecimal vendido = BigDecimal.ZERO;

			String puntoVentaId = rset1.getString("puntoventaId");			
			puntoVentaIds.append(", " + puntoVentaId);

			Integer qtdevendido = rset1.getInt("qtdevendido");
			Integer qtdedigitado = rset1.getInt("qtdedigitado");
			Integer qtdeAnulado = rset1.getInt("qtdeAnulado");
			Integer qtdeDevolvido = rset1.getInt("qtdeDevolvido");
			
			if (isDiario) {
				dataInicial = rset1.getString("DATA");
				dataFinal = rset1.getString("DATA");
			}		
					
			vendido = rset1.getBigDecimal("vendido");
			BigDecimal digitado = rset1.getBigDecimal("digitado");
			BigDecimal anulado = rset1.getBigDecimal("anulado");
			BigDecimal devolvido = rset1.getBigDecimal("devolvido");

			total = preencherAgenciaFechamento(empresaNome, rset1, agenciaFechamento, total, vendido, qtdevendido, qtdedigitado, qtdeAnulado, qtdeDevolvido, digitado, anulado, devolvido);

			carregarDadosReceitaDespesa(dataInicial, dataFinal, isDiario, empresa, sdf, agenciaFechamento, total, vendido, puntoVentaId);
			lsDadosRelatorio.add(agenciaFechamento);
		}
		
		//Comentado para atender ao mantis 23925
		//inserirReceitaDespesa(fecInicio, fecFinal, empresa, puntoVentaIds, empresaNome);

	}

	public void setLsDadosRelatorio(List<RelatorioAgenciaFechamentoAntigoBean> lsDadosRelatorio) {
		this.setCollectionDataSource(new JRBeanCollectionDataSource(lsDadosRelatorio));
		this.lsDadosRelatorio = lsDadosRelatorio;
	}

	/**
	 * @param fecInicio
	 * @param fecFinal
	 * @param empresa
	 * @param sdf
	 * @param agenciaFechamento
	 * @param total
	 * @param vendido
	 * @param puntoVentaId
	 * @throws SQLException
	 * @throws ParseException
	 */
	protected void carregarDadosReceitaDespesa(String fecInicio, String fecFinal, Boolean isDiario, String empresa, SimpleDateFormat sdf, RelatorioAgenciaFechamentoAntigoBean agenciaFechamento, BigDecimal total, BigDecimal vendido, String puntoVentaId) throws SQLException, ParseException {
		NamedParameterStatement stmt = new NamedParameterStatement(getConexao(), criarQueryReceitaDespesa(puntoVentaId, empresa));
		if (isDiario) {
			fecInicio = fecInicio.substring(0, 10).toString() + " 00:00:00";
			fecFinal = fecFinal.substring(0, 10).toString() + " 23:59:59";
		}

		stmt.setTimestamp("de", new java.sql.Timestamp(sdf.parse(fecInicio).getTime()));
		stmt.setTimestamp("ate", new java.sql.Timestamp(sdf.parse(fecFinal).getTime()));

		ResultSet rset2 = stmt.executeQuery();

		if (rset2.next()) {

			Integer qtdedespesa = rset2.getInt("qtdedespesa");
			BigDecimal receita = rset2.getBigDecimal("receita");
			BigDecimal despesa = rset2.getBigDecimal("despesa");

			agenciaFechamento.setQtdeDespesa(qtdedespesa != null ? qtdedespesa : NumberUtils.INTEGER_ZERO);
			agenciaFechamento.setReceita(receita != null ? receita : BigDecimal.ZERO);
			agenciaFechamento.setDespesa(despesa != null ? despesa : BigDecimal.ZERO);

			total = total.add(receita != null ? receita : BigDecimal.ZERO);
			total = total.subtract(despesa != null ? despesa : BigDecimal.ZERO);

			agenciaFechamento.setVendido(vendido != null ? vendido : BigDecimal.ZERO);
			agenciaFechamento.setTotal(total);

		} else {
			agenciaFechamento.setQtdeDespesa(NumberUtils.INTEGER_ZERO);
			agenciaFechamento.setReceita(BigDecimal.ZERO);
			agenciaFechamento.setDespesa(BigDecimal.ZERO);
			agenciaFechamento.setVendido(vendido != null ? vendido : BigDecimal.ZERO);
			agenciaFechamento.setTotal(total);
		}

		// Estava dando um erro de "máximo de cursores abertos excedido"
		// Colocado isso o erro parou de ocorrer
		rset2.getStatement().close();
		rset2.close();
	}
	
	private BigDecimal preencherAgenciaFechamento(String empresaNome, ResultSet rset1, RelatorioAgenciaFechamentoAntigoBean agenciaFechamento, BigDecimal total, BigDecimal vendido, Integer qtdevendido, Integer qtdedigitado, Integer qtdeAnulado, Integer qtdeDevolvido, BigDecimal digitado, BigDecimal anulado, BigDecimal devolvido) throws SQLException {
		Integer qtdeTotal;
		agenciaFechamento.setEmpresa(empresaNome);

		agenciaFechamento.setCodigo(rset1.getString("codigo"));
		agenciaFechamento.setDescricao(rset1.getString("descricao"));
		if (TipoRelatorioFechamentoEnum.DIARIO.equals(tipoRelatorio)) {
			agenciaFechamento.setData(rset1.getDate("data"));
		}
		if (!TipoRelatorioFechamentoEnum.ANTIGO.equals(tipoRelatorio)) {
			agenciaFechamento.setSubordinado(rset1.getString("subordinado"));
		}
		agenciaFechamento.setQtdeVendido(qtdevendido != null ? qtdevendido : NumberUtils.INTEGER_ZERO);
		agenciaFechamento.setQtdeDigitado(qtdedigitado != null ? qtdedigitado : NumberUtils.INTEGER_ZERO);
		agenciaFechamento.setQtdeAnulado(qtdeAnulado != null ? qtdeAnulado : NumberUtils.INTEGER_ZERO);
		agenciaFechamento.setQtdeDevolvido(qtdeDevolvido != null ? qtdeDevolvido : NumberUtils.INTEGER_ZERO);

		qtdeTotal = ((qtdevendido != null ? qtdevendido : NumberUtils.INTEGER_ZERO) + (qtdedigitado != null ? qtdedigitado : NumberUtils.INTEGER_ZERO))
				- ((qtdeAnulado != null ? qtdeAnulado : NumberUtils.INTEGER_ZERO) + (qtdeDevolvido != null ? qtdeDevolvido : NumberUtils.INTEGER_ZERO));

		if (qtdeTotal < 0) {
			qtdeTotal = NumberUtils.INTEGER_ZERO;
		}

		agenciaFechamento.setQtdeTotal(qtdeTotal);

		agenciaFechamento.setDigitado(digitado != null ? digitado : BigDecimal.ZERO);
		agenciaFechamento.setAnulado(anulado != null ? anulado : BigDecimal.ZERO);
		agenciaFechamento.setDevolvido(devolvido != null ? devolvido : BigDecimal.ZERO);

		total = total.add(vendido != null ? vendido : BigDecimal.ZERO);
		total = total.add(digitado != null ? digitado : BigDecimal.ZERO);
		total = total.subtract(anulado != null ? anulado : BigDecimal.ZERO);
		total = total.subtract(devolvido != null ? devolvido : BigDecimal.ZERO);
		return total;
	}
	
	private ResultSet criaResultSet(String fecInicio, String fecFinal, SimpleDateFormat sdf, NamedParameterStatement stmt) throws SQLException, ParseException {
		stmt.setTimestamp("de", new java.sql.Timestamp(sdf.parse(fecInicio).getTime()));
		stmt.setTimestamp("ate", new java.sql.Timestamp(sdf.parse(fecFinal).getTime()));

		ResultSet rset1 = stmt.executeQuery();
		rset1.setFetchSize(500);
		
		return rset1;
	}
	
	private void inserirReceitaDespesa(String fecInicio, String fecFinal, String empresa, StringBuilder puntoVentaIds, String empresaNome) {
		NamedParameterStatement stmt;
		try {
			stmt = new NamedParameterStatement(getConexao(), criarSQLBuscaReceitaDespesa(puntoVentaIds, empresa));

			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");

			ResultSet rset1 = criaResultSet(fecInicio, fecFinal, sdf, stmt);

			while (rset1.next()) {
				RelatorioAgenciaFechamentoAntigoBean agenciaFechamento = new RelatorioAgenciaFechamentoAntigoBean();
				BigDecimal total = BigDecimal.ZERO;
				BigDecimal vendido = BigDecimal.ZERO;

				Integer qtdevendido = rset1.getInt("qtdevendido");
				Integer qtdedigitado = rset1.getInt("qtdedigitado");
				Integer qtdeAnulado = rset1.getInt("qtdeAnulado");
				Integer qtdeDevolvido = rset1.getInt("qtdeDevolvido");

				vendido = rset1.getBigDecimal("vendido");
				BigDecimal digitado = rset1.getBigDecimal("digitado");
				BigDecimal anulado = rset1.getBigDecimal("anulado");
				BigDecimal devolvido = rset1.getBigDecimal("devolvido");

				total = preencherAgenciaFechamento(empresaNome, rset1, agenciaFechamento, total, vendido, qtdevendido, qtdedigitado, qtdeAnulado, qtdeDevolvido, digitado, anulado, devolvido);

				Integer qtdedespesa = rset1.getInt("qtdedespesa");
				BigDecimal receita = rset1.getBigDecimal("receita");
				BigDecimal despesa = rset1.getBigDecimal("despesa");

				agenciaFechamento.setQtdeDespesa(qtdedespesa != null ? qtdedespesa : NumberUtils.INTEGER_ZERO);
				agenciaFechamento.setReceita(receita != null ? receita : BigDecimal.ZERO);
				agenciaFechamento.setDespesa(despesa != null ? despesa : BigDecimal.ZERO);

				total = total.add(receita != null ? receita : BigDecimal.ZERO);
				total = total.subtract(despesa != null ? despesa : BigDecimal.ZERO);

				agenciaFechamento.setVendido(vendido != null ? vendido : BigDecimal.ZERO);
				agenciaFechamento.setTotal(total);
				
				lsDadosRelatorio.add(agenciaFechamento);
			}

		} catch (SQLException e) {
			log.error("Erro ao adicionar receita e despesa: ", e);
		} catch (ParseException e) {
			log.error("Erro ao adicionar receita e despesa: ", e);
		}
	}
	
	private String criarSQLBuscaReceitaDespesa(StringBuilder puntoVentaIds, String empresa) {

		StringBuilder sql = new StringBuilder();
		sql.append(" SELECT  ");
		sql.append(" 		pv.NUMPUNTOVENTA AS codigo, pv.nombpuntoventa AS descricao, PV_GERENCIA.NUMPUNTOVENTA AS subordinado,      ");
		sql.append(" 		NULL as qtdevendido, NULL as qtdedigitado, NULL as qtdeanulado, NULL as qtdedevolvido,     ");
		sql.append(" 		NULL as vendido, NULL as digitado, NULL as anulado, NULL as devolvido, cd.puntoventa_id,     ");;
		sql.append(" 		COALESCE(COUNT(CASE WHEN cdp.IMPORTE < 0 THEN 1 ELSE NULL END),0) as qtdedespesa,     ");
		sql.append(" 		COALESCE(SUM(CASE WHEN cdp.IMPORTE > 0 THEN cdp.IMPORTE ELSE NULL END),0) AS receita,    ");
		sql.append(" 		COALESCE(ABS(SUM(CASE WHEN cdp.IMPORTE < 0 THEN cdp.IMPORTE ELSE NULL END)),0) AS despesa    ");
		
		boolean isDiario = TipoRelatorioFechamentoEnum.DIARIO.equals(tipoRelatorio);
		
		if (isDiario) {
			sql.append(" , cd.fechorvta AS data ");
		}
				
		sql.append(" 	FROM CAJA_DIVERSOS cd, ");
		sql.append(" 		CAJA_DIVERSOS_PAGO cdp, ");
		sql.append(" 		EVENTO_EXTRA ee, ");
		sql.append(" 		punto_venta pv ");
		sql.append(" 		LEFT JOIN punto_venta   pv_gerencia ON pv_gerencia.puntoventa_id = pv.puntoventapadre_id ");
		sql.append("    WHERE cd.MOTIVOCANCELACION_ID IS NULL ");
		sql.append("  	 AND ee.EVENTOEXTRA_ID = cd.EVENTOEXTRA_ID ");
		sql.append("   	 AND cdp.CAJADIVERSOS_ID = cd.CAJADIVERSOS_ID ");
		sql.append("   	 AND ee.activo = 1 ");
		sql.append("   	 AND cd.TURNO_ID IS NOT NULL ");
		sql.append("     AND cd.FECCORTE BETWEEN :de AND :ate ");
		sql.append("     AND cd.puntoventa_id = pv.puntoventa_id ");
		sql.append("     AND  pv.puntoventa_id in ( " + puntoVentaIds.toString() + ") ");

		if (empresa != null && !empresa.equals("") && !empresa.equals("-1")) {
			sql.append("     AND ee.EMPRESA_ID = " + empresa + "                       ");
		}
		
		sql.append(" group by cd.puntoventa_id, pv.NUMPUNTOVENTA, pv.nombpuntoventa, PV_GERENCIA.NUMPUNTOVENTA ");
		
		if (isDiario) {
			sql.append(" , cd.fechorvta ");
		}

		return sql.toString();

	}

}
