package com.rjconsultores.ventaboletos.relatorios.impl;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.rjconsultores.ventaboletos.relatorios.utilitarios.ArrayDataSource;
import com.rjconsultores.ventaboletos.relatorios.utilitarios.Relatorio;
import com.rjconsultores.ventaboletos.utilerias.DateUtil;
import com.rjconsultores.ventaboletos.utilerias.MoneyHelper;
import com.rjconsultores.ventaboletos.vo.parada.ParadaVO;
import com.rjconsultores.ventaboletos.vo.ruta.RutaVO;
import com.rjconsultores.ventaboletos.web.utilerias.NamedParameterStatement;

public class RelatorioDesempenhoPorLinha extends Relatorio {

	private static Logger log = LogManager.getLogger(RelatorioDesempenhoPorLinha.class);

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

		this.setCustomDataSource(new ArrayDataSource(this) {
			@Override
			public void initDados() throws Exception {

				Connection conexao = this.relatorio.getConexao();
				Map<String, Object> parametros = this.relatorio.getParametros();
				@SuppressWarnings("unchecked")
				Map<Integer, List<RutaVO>> mapRutaTrecho = (Map<Integer, List<RutaVO>>) parametros.get("MAPRUTATRECHO");

				if (parametros.get("TRECHOS") != null) {
					for (Entry<Integer, List<RutaVO>> entry : mapRutaTrecho.entrySet()) {
						String sqlCombinacoes = gerarCombinacaoTrechos(parametros);
						StringBuilder sql = getSQL(parametros, sqlCombinacoes, entry.getKey());
						NamedParameterStatement stmt = new NamedParameterStatement(conexao, sql.toString());
						setarParametrosDaConsulta(parametros, stmt);
						ResultSet rset2 = stmt.executeQuery();
						setarResult(rset2);
					}
				} else {
					StringBuilder sql = getSQL(parametros, null, null);
					NamedParameterStatement stmt = new NamedParameterStatement(conexao, sql.toString());
					setarParametrosDaConsulta(parametros, stmt);
					ResultSet rset2 = stmt.executeQuery();
					setarResult(rset2);
				}
			}

			private void setarParametrosDaConsulta(Map<String, Object> parametros, NamedParameterStatement stmt)
					throws SQLException {
				stmt.setInt("EMPRESA_ID", Integer.valueOf(parametros.get("EMPRESA_ID").toString()));

				stmt.setTimestamp("DATA_INICIAL",
						new Timestamp(DateUtil.inicioFecha((Date) parametros.get("DATA_INICIAL")).getTime()));
				stmt.setTimestamp("DATA_FINAL",
						new Timestamp(DateUtil.fimFecha((Date) parametros.get("DATA_FINAL")).getTime()));
			}

			private void setarResult(ResultSet rset2) throws SQLException {
				while (rset2.next()) {
					Map<String, Object> dataResult = new HashMap<String, Object>();

					dataResult.put("DATA", rset2.getString("data"));
					dataResult.put("PREFIXO", rset2.getString("prefixo"));
					dataResult.put("ITINERARIO", rset2.getString("itinerario"));
					dataResult.put("HORA", rset2.getString("hora"));
					dataResult.put("TARIFASARRECADA", rset2.getBigDecimal("tarifasArrecada"));
					dataResult.put("QTDPASSAGENS", rset2.getInt("qtdPassagens"));
					dataResult.put("QTDVIAGENS", 1);
					dataResult.put("EXTENSAO", rset2.getInt("extensao"));
					dataResult.put("TOTALKM", rset2.getInt("extensao"));
					dataResult.put("MEDIAKM", MoneyHelper.arredondar(rset2.getBigDecimal("mediaKm")));

					this.dados.add(dataResult);
				}
			}

			private StringBuilder getSQL(Map<String, Object> parametros, String sqlCombinacoes, Integer rutaId) {

				StringBuilder sql = new StringBuilder();

				sql.append("	SELECT	");
				sql.append("	    c.corrida_id,	");
				sql.append("	    to_char(c.fechorsalida,'DD/MM/YYYY') as data,	");
				sql.append("	    to_char(c.fechorsalida, 'HH24:MI') AS hora,	");
				sql.append("	    r.prefixo prefixo,	");
				sql.append("	    r.descruta itinerario,	");
				sql.append("	    coalesce(SUM(cj.preciopagado),0)  tarifasArrecada,	");
				sql.append("	    COUNT(cj.boleto_id) qtdPassagens,  	");
				sql.append("	    tr.cantkmreal extensao,	");
				sql.append("	    coalesce(SUM(cj.preciopagado),0) / coalesce(tr.cantkmreal,0) as mediaKm	");
				sql.append("	FROM	");
				sql.append("	    corrida c	");
				sql.append("	    INNER JOIN ruta             r ON r.ruta_id = c.ruta_id	");
				sql.append("	    INNER JOIN parada           origem ON ( c.origen_id = origem.parada_id )	");
				sql.append("	    INNER JOIN parada           destino ON ( c.destino_id = destino.parada_id )	");
				sql.append("	    INNER JOIN ruta_combinacion rc ON ( rc.ruta_id = c.ruta_id	");
				sql.append("	                                        AND rc.activo = 1 )	");
				sql.append("	    INNER JOIN tramo            tr ON ( tr.tramo_id = rc.tramo_id	");
				sql.append("	                             AND tr.origen_id = c.origen_id	");
				sql.append("	                             AND tr.destino_id = c.destino_id )	");
				sql.append("  inner join MARCA m ");
				sql.append("    on m.marca_id = c.marca_id ");
				sql.append("    and m.activo = 1 ");
				sql.append("	    LEFT JOIN (	");
				sql.append("	        SELECT	");
				sql.append("	            co.corrida_id corrida_id,	");
				sql.append("	            co.feccorrida feccorrida,	");
				sql.append("	            bo.boleto_id,	");
				sql.append("	            bo.preciopagado	");
				sql.append("	        FROM	");
				sql.append("	                 corrida co	");
				sql.append("	            INNER JOIN boleto           bo ON co.corrida_id = bo.corrida_id	");
				sql.append("	                                    AND co.feccorrida = bo.feccorrida	");
				sql.append("	                                    AND bo.activo = 1	");
				sql.append("	            INNER JOIN ruta_combinacion rc ON rc.ruta_id = co.ruta_id	");
				sql.append("	            INNER JOIN tramo            t ON rc.tramo_id = t.tramo_id	");
				sql.append("	                                  AND t.origen_id = bo.origen_id	");
				sql.append("	                                  AND t.destino_id = bo.destino_id	");
				sql.append("	            INNER JOIN ruta             r ON r.ruta_id = co.ruta_id	");
				sql.append("	            INNER JOIN ruta_combinacion rc2 ON ( rc2.ruta_id = r.ruta_id	");
				sql.append("	                                                 AND rc2.activo = 1 )	");
				sql.append("	            INNER JOIN tramo            tr ON ( tr.tramo_id = rc2.tramo_id	");
				sql.append("	                                     AND tr.origen_id = co.origen_id	");
				sql.append("	                                     AND tr.destino_id = co.destino_id )	");
				sql.append("	        WHERE	");
				sql.append("	            co.activo NOT IN ( 0, 2 )	");
				sql.append("	            AND rc.activo = 1	");
				sql.append("	            AND t.activo = 1	");
				sql.append("	            AND bo.indstatusboleto != 'S'	");
				sql.append("	            AND bo.motivocancelacion_id IS NULL	");
//								sql.append("	            AND bo.categoria_id <> :crianca_id	");
				sql.append("    and co.FECCORRIDA >= :DATA_INICIAL ");
				sql.append("    and co.FECCORRIDA <= :DATA_FINAL ");
				sql.append("	        GROUP BY	");
				sql.append("	            co.corrida_id,	");
				sql.append("	            co.feccorrida,	");
				sql.append("	            bo.boleto_id,	");
				sql.append("	            bo.preciopagado	");
				sql.append("	    )                cj ON ( cj.corrida_id = c.corrida_id	");
				sql.append("	              AND cj.feccorrida = c.feccorrida )	");
				sql.append("	WHERE	");
				sql.append("    m.EMPRESA_ID  = :EMPRESA_ID ");
				sql.append("    and c.FECCORRIDA >= :DATA_INICIAL ");
				sql.append("    and c.FECCORRIDA <= :DATA_FINAL ");

				if (parametros.get("LINHAS") != null && !possuiFiltroTodos("LINHAS")
						&& parametros.get("TRECHOS") != null) {
					sql.append("    and c.ruta_id IN (" + rutaId + ")");
				} else if (parametros.get("LINHAS") != null && !possuiFiltroTodos("LINHAS")) {
					sql.append("    and c.ruta_id IN (" + parametros.get("LINHAS").toString() + ")");
				}
				if (sqlCombinacoes != null && !sqlCombinacoes.isEmpty()) {
					sql.append("    " + sqlCombinacoes + " ");
				}

				sql.append("	GROUP BY	");
				sql.append("	    c.corrida_id,	");
				sql.append("	    c.fechorsalida,	");
				sql.append("	    to_char(c.fechorsalida, 'HH24:MI'),	");
				sql.append("	    r.descruta,	");
				sql.append("	    r.prefixo,	");
				sql.append("	    tr.cantkmreal	");
				sql.append("	    order by  c.fechorsalida asc	");

				return sql;
			}

		});
	}

	@Override
	protected void processaParametros() throws Exception {

	}

	private String gerarCombinacaoTrechos(Map<String, Object> parametros) {

		@SuppressWarnings("unchecked")
		List<RutaVO> trechos = (List<RutaVO>) parametros.get("TRECHOS");

		List<ParadaVO> listParada1 = new ArrayList<>();
		List<ParadaVO> listParada2 = new ArrayList<>();
		StringBuilder sql = new StringBuilder();

		for (RutaVO tramoVO : trechos) {
			listParada1.add(new ParadaVO(tramoVO.getOrigenId(), tramoVO.getRutaId().intValue()));
		}

		listParada2.addAll(listParada1);

		if (trechos != null) {

			LinkedList<List<ParadaVO>> lists = new LinkedList<List<ParadaVO>>();

			lists.add(listParada1);
			lists.add(listParada2);

			Set<String> combinacoes = new TreeSet<String>();

			sql.append("AND (");
			for (ParadaVO s : lists.removeFirst())
				combinacoes.add(s.getParadaId().toString());

			while (!lists.isEmpty()) {
				List<ParadaVO> next = lists.removeFirst();
				Set<String> novasCombinacoes = new TreeSet<String>();
				for (String s1 : combinacoes) {
					for (ParadaVO s2 : next) {
						novasCombinacoes.add(s1 + ";" + s2.getParadaId());
						sql.append("(c.origen_id = ").append(s1).append(" AND c.destino_id = ").append(s2.getParadaId())
								.append(" and c.ruta_id = " + s2.getRutaId() + " ) OR ");
					}
				}
				combinacoes = novasCombinacoes;
			}

			// Remover o último " OR " da string
			if (combinacoes.size() > 0) {
				sql.delete(sql.length() - 4, sql.length());
			}
			sql.append(")");

		}

		return sql.toString();

	}

}
