package com.rjconsultores.ventaboletos.relatorios.impl;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.rjconsultores.ventaboletos.constantes.Constantes;
import com.rjconsultores.ventaboletos.relatorios.utilitarios.DataSource;
import com.rjconsultores.ventaboletos.relatorios.utilitarios.Relatorio;
import com.rjconsultores.ventaboletos.utilerias.DateUtil;
import com.rjconsultores.ventaboletos.vo.comissao.MovimentosAtrasoVO;
import com.rjconsultores.ventaboletos.vo.comissao.MovimentosAtrasoVO.SituacaoDiaMovimento;
import com.rjconsultores.ventaboletos.web.utilerias.NamedParameterStatement;

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

public class RelatorioMovimentosAtraso extends Relatorio {
	
	private static Logger log = Logger.getLogger(RelatorioMovimentosAtraso.class);
	private Integer empresaId;
	private Integer puntoventaId;
	private Date dataInicial;
	private Date dataFinal;
	private String competencia;
	private SituacaoDiaMovimento status;
	private Map<Integer, Set<Integer>> diasComMovimentos;
	
	public RelatorioMovimentosAtraso(Map<String, Object> parametros, Connection conexao) throws Exception {
		super(parametros, conexao);
		
		this.setCustomDataSource(new DataSource(this) {
			@Override
			public void initDados() throws Exception {
				Connection conexao = this.relatorio.getConexao();
				definirFiltros(this.relatorio.getParametros());
				List<MovimentosAtrasoVO> lsDadosRelatorio = processarRelatorio(conexao);
				setLsDadosRelatorio(lsDadosRelatorio);
			}
		});
	}

	private List<MovimentosAtrasoVO> processarRelatorio(Connection conexao) throws ParseException {
		carregarDiasComMovimentos(conexao);
		List<MovimentosAtrasoVO> lsDadosRelatorio = iniciarDados(conexao);
		processarDiasConferidos(conexao, lsDadosRelatorio);
		removerMovimentosSemAtraso(lsDadosRelatorio);
		Collections.sort(lsDadosRelatorio);
		return lsDadosRelatorio;
	}

	private void removerMovimentosSemAtraso(List<MovimentosAtrasoVO> lsDadosRelatorio) {
		List<MovimentosAtrasoVO> lsMovimentosSemAtraso = new ArrayList<MovimentosAtrasoVO>();
		for (MovimentosAtrasoVO movimentosAtraso : lsDadosRelatorio) {
			if(movimentosAtraso.getDiasEmAtraso() == null || movimentosAtraso.getDiasEmAtraso().isEmpty()) {
				lsMovimentosSemAtraso.add(movimentosAtraso);
			}
		}
		
		lsDadosRelatorio.removeAll(lsMovimentosSemAtraso);
	}

	private void processarDiasConferidos(Connection conexao, List<MovimentosAtrasoVO> lsDadosRelatorio) {
		ResultSet rset = null;
		NamedParameterStatement stmt = null;
		try {
			StringBuilder sQuery = new StringBuilder();
			sQuery.append("SELECT DISTINCT PV.PUNTOVENTA_ID, CO.DATAMOVIMENTO ")
				  .append("FROM CONFERENCIA CO ")
				  .append("JOIN PUNTO_VENTA PV ON PV.PUNTOVENTA_ID = CO.PUNTOVENTA_ID ")
				  .append("WHERE CO.ACTIVO = 1 ")
				  .append("AND CO.DATAMOVIMENTO BETWEEN :dataInicial AND :dataFinal ")
				  .append("AND CO.INDMALOTERECEBIDO = 1 ");
			
			if(empresaId != null) {
				sQuery.append("AND CO.EMPRESA_ID = :empresaId ");
			}

			if(puntoventaId != null) {
				sQuery.append("AND CO.PUNTOVENTA_ID = :puntoventaId ");
			}

			sQuery.append("ORDER BY PV.PUNTOVENTA_ID, CO.DATAMOVIMENTO");
			
			log.info(sQuery.toString());
			
			stmt = new NamedParameterStatement(conexao, sQuery.toString());
			if(empresaId != null) {
				stmt.setInt("empresaId", empresaId);
			}
			
			if(puntoventaId != null) {
				stmt.setInt("puntoventaId", puntoventaId);
			}
			stmt.setDate("dataInicial", new java.sql.Date(dataInicial.getTime()));
			stmt.setDate("dataFinal", new java.sql.Date(dataFinal.getTime()));
			
			rset = stmt.executeQuery();
			
			while (rset.next()) {
				MovimentosAtrasoVO movimentosAtraso = new MovimentosAtrasoVO(rset.getInt("PUNTOVENTA_ID"),DateUtil.normalizar(rset.getDate("DATAMOVIMENTO")));
				if(lsDadosRelatorio.contains(movimentosAtraso)) {
					MovimentosAtrasoVO movimentosAtrasoAux = lsDadosRelatorio.get(lsDadosRelatorio.indexOf(movimentosAtraso));
					if(movimentosAtrasoAux.getDiasEmAtraso() != null && !movimentosAtrasoAux.getDiasEmAtraso().isEmpty()) {
						movimentosAtrasoAux.getDiasEmAtraso().remove(DateUtil.getIntegerDate(movimentosAtraso.getDataMovimento(), "dd"));
					}
				}
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			try {
				if(rset != null) {
					rset.close();
				}
				if(stmt != null) {
					stmt.close();
				}
			} catch (SQLException e) {
				log.error(e.getMessage(), e);
			}
		}
	}
	

	private void validarPuntoventaDiasComMovimentos(Integer puntoventaId) {
		if(!diasComMovimentos.containsKey(puntoventaId)) {
			diasComMovimentos.put(puntoventaId, new TreeSet<Integer>());
		}
	}

	private void carregarDiasComMovimentos(Connection conexao) {
		ResultSet rset = null;
		NamedParameterStatement stmt = null;
		try {
			StringBuilder sQuery = new StringBuilder();
			sQuery.append("SELECT DISTINCT B.PUNTOVENTA_ID, TO_DATE(B.FECHORVENTA,'DD/MM/YYYY') AS FECHORVENTA ")
				  .append("FROM BOLETO B ")
				  .append("WHERE B.ACTIVO = 1 ")
				  .append("AND TO_DATE(B.FECHORVENTA,'DD/MM/YYYY') BETWEEN TO_DATE(:dataInicial,'DD/MM/YYYY') AND TO_DATE(:dataFinal,'DD/MM/YYYY') ")
				  .append("AND B.TIPOVENTA_ID IN (")
				  .append(Arrays.asList(Constantes.TPV_BOLETO_REMOTO,Constantes.TPV_MANUAL,Constantes.TPV_DIRECTO_NORMAL, Constantes.TPV_TRANSFERENCIAS).toString().replaceAll("[^0-9,]", ""))
				  .append(") ")
				  .append("AND ( ")
				  .append("		(B.INDSTATUSBOLETO = 'V' AND (B.MOTIVOCANCELACION_ID IS NULL OR B.MOTIVOCANCELACION_ID IN (")
				  .append(Arrays.asList(Constantes.MVO_CANCEL_CANCELACION,Constantes.MVO_CANCEL_DEVOLUCAO,Constantes.MVO_CANCEL_TROCA).toString().replaceAll("[^0-9,]", ""))
				  .append("		))) ")
				  .append("		OR ")
				  .append("		(B.INDSTATUSBOLETO = 'C' AND B.MOTIVOCANCELACION_ID IN (")
				  .append(Arrays.asList(Constantes.MVO_CANCEL_CANCELACION,Constantes.MVO_CANCEL_DEVOLUCAO).toString().replaceAll("[^0-9,]", ""))
				  .append("		)) ")
				  .append("		OR ")
				  .append("		(B.INDSTATUSBOLETO = 'T' AND (B.MOTIVOCANCELACION_ID IS NULL OR B.MOTIVOCANCELACION_ID IN (")
				  .append(Arrays.asList(Constantes.MVO_CANCEL_CANCELACION,Constantes.MVO_CANCEL_DEVOLUCAO).toString().replaceAll("[^0-9,]", ""))
				  .append("		))) ")
				  .append("	   ) ");
			
			if(empresaId != null) {
				sQuery.append("AND B.EMPRESACORRIDA_ID = :empresaId ");
			}

			if(puntoventaId != null) {
				sQuery.append("AND B.PUNTOVENTA_ID = :puntoventaId ");
			}

			log.info(sQuery.toString());
			
			stmt = new NamedParameterStatement(conexao, sQuery.toString());
			if(empresaId != null) {
				stmt.setInt("empresaId", empresaId);
			}
			
			if(puntoventaId != null) {
				stmt.setInt("puntoventaId", puntoventaId);
			}
			stmt.setDate("dataInicial", new java.sql.Date(dataInicial.getTime()));
			stmt.setDate("dataFinal", new java.sql.Date(dataFinal.getTime()));
			
			rset = stmt.executeQuery();
			
			diasComMovimentos = new HashMap<Integer, Set<Integer>>();
			while (rset.next()) {
				Integer puntoventaId = rset.getInt("PUNTOVENTA_ID");
				Date fechorventa = DateUtil.normalizar(rset.getDate("FECHORVENTA"));
				
				validarPuntoventaDiasComMovimentos(puntoventaId);
				diasComMovimentos.get(puntoventaId).add(DateUtil.getIntegerDate(fechorventa, "dd"));
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			try {
				if(rset != null) {
					rset.close();
				}
				if(stmt != null) {
					stmt.close();
				}
			} catch (SQLException e) {
				log.error(e.getMessage(), e);
			}
		}
	}

	private List<MovimentosAtrasoVO> iniciarDados(Connection conexao) {
		ResultSet rset = null;
		NamedParameterStatement stmt = null;
		try {
			List<MovimentosAtrasoVO> lsMovimentosAtrasoVOs = new ArrayList<MovimentosAtrasoVO>();
			
			StringBuilder sQuery = new StringBuilder();
			sQuery.append("SELECT DISTINCT PV.PUNTOVENTA_ID, PV.NUMPUNTOVENTA, PV.NOMBPUNTOVENTA, NVL(FP.DIASEMTRANSITO,0) AS DIASEMTRANSITO, ")
				  .append("ES.CVEESTADO AS UF, FP.INTERVALOFECHAMENTO, PVP.NOMBPUNTOVENTA AS NOMPUNTOVENTAPADRE, PV.NUMTELEFONOUNO ")
				  .append("FROM PUNTO_VENTA PV ")
				  .append("LEFT JOIN PUNTO_VENTA PVP ON PVP.PUNTOVENTA_ID = PV.PUNTOVENTAPADRE_ID ")
				  .append("INNER JOIN PTOVTA_EMPRESA PTE ON PTE.PUNTOVENTA_ID = PV.PUNTOVENTA_ID ")
				  .append("LEFT JOIN COLONIA CO ON CO.COLONIA_ID = PV.COLONIA_ID ")
				  .append("LEFT JOIN CIUDAD CID ON CID.CIUDAD_ID = CO.CIUDAD_ID ")
				  .append("LEFT JOIN ESTADO ES ON ES.ESTADO_ID = CID.ESTADO_ID ")
				  .append("LEFT JOIN FECHAMENTO_PARAMPTOVTA FP ON FP.EMPRESA_ID = PTE.EMPRESA_ID AND PV.PUNTOVENTA_ID = FP.PUNTOVENTA_ID AND FP.ACTIVO = 1 ")
				  .append("WHERE PV.ACTIVO = 1 ");
			
			if(empresaId != null) {
				sQuery.append("AND PTE.EMPRESA_ID = :empresaId ");
			}

			if(puntoventaId != null) {
				sQuery.append("AND PV.PUNTOVENTA_ID = :puntoventaId ");
			}

			sQuery.append("ORDER BY PV.NOMBPUNTOVENTA ");
			
			log.info(sQuery.toString());
			
			stmt = new NamedParameterStatement(conexao, sQuery.toString());
			if(empresaId != null) {
				stmt.setInt("empresaId", empresaId);
			}

			if(puntoventaId != null) {
				stmt.setInt("puntoventaId", puntoventaId);
			}

			rset = stmt.executeQuery();
			while (rset.next()) {
				MovimentosAtrasoVO movimentosAtraso = new MovimentosAtrasoVO();
				movimentosAtraso.setPuntoventaId(rset.getInt("PUNTOVENTA_ID"));
				movimentosAtraso.setNumPuntoVenta(rset.getString("NUMPUNTOVENTA"));
				movimentosAtraso.setNombpuntoventa(rset.getString("NOMBPUNTOVENTA"));
				movimentosAtraso.setCompetencia(competencia);
				movimentosAtraso.setDiasemtransito(rset.getInt("DIASEMTRANSITO"));
				movimentosAtraso.setIntervalofechamento(rset.getInt("INTERVALOFECHAMENTO"));
				movimentosAtraso.setNombpuntoventaPadre(rset.getString("NOMPUNTOVENTAPADRE"));
				movimentosAtraso.setUf(rset.getString("UF"));
				movimentosAtraso.setNumtelefonouno(rset.getString("NUMTELEFONOUNO"));
				carregarDiasMovimento(movimentosAtraso);
				if(!movimentosAtraso.getDiasEmAtraso().isEmpty()) {
					lsMovimentosAtrasoVOs.add(movimentosAtraso);
				}
			}

			return lsMovimentosAtrasoVOs;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			try {
				if(rset != null) {
					rset.close();
				}
				if(stmt != null) {
					stmt.close();
				}
			} catch (SQLException e) {
				log.error(e.getMessage(), e);
			}
		}
		return null;
	}

	private void carregarDiasMovimento(MovimentosAtrasoVO movimentosAtrasoBase) throws ParseException {
		movimentosAtrasoBase.setDiasEmAtraso(new TreeSet<Integer>());
		Set<Integer> diasCompetencia = DateUtil.carregarDiasCompetencia(movimentosAtrasoBase.getCompetencia());
		Date dataAtual = DateUtil.normalizar(new Date());
		Integer diasEmTransito = movimentosAtrasoBase.getDiasemtransito() != null ? movimentosAtrasoBase.getDiasemtransito() : 0;
		diasEmTransito += movimentosAtrasoBase.getIntervalofechamento() != null ? movimentosAtrasoBase.getIntervalofechamento() : 0;
		for (Integer dia : diasCompetencia) {
			Date dataMovimento = DateUtil.normalizar(DateUtil.getDateFromString(dia+"/"+movimentosAtrasoBase.getCompetencia(), "dd/MM/yyyy"));
			if(dataAtual.compareTo(dataMovimento) >= 0 && DateUtil.getElapsedDias(dataAtual, dataMovimento) > diasEmTransito) {
				movimentosAtrasoBase.getDiasEmAtraso().add(dia);
			}
		}
		
		if(diasComMovimentos.containsKey(movimentosAtrasoBase.getPuntoventaId())) {
			movimentosAtrasoBase.getDiasEmAtraso().retainAll(diasComMovimentos.get(movimentosAtrasoBase.getPuntoventaId()));
		} else {
			movimentosAtrasoBase.getDiasEmAtraso().clear();
		}
		
	}

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

	@Override
	protected void processaParametros() throws Exception {
	}
	
	private void definirFiltros(Map<String, Object> parametros) throws ParseException {
		Integer puntoventaId = (Integer) parametros.get("puntoventaId");
		Integer empresaId = (Integer) parametros.get("empresaId");
		String competencia = (String) parametros.get("competencia");
		status = (SituacaoDiaMovimento) parametros.get("status");
		
		if(StringUtils.isNotBlank(competencia)) {
			this.competencia = competencia;
		}
		
		if(empresaId != null) {
			this.empresaId = empresaId;
		}
		if(puntoventaId != null) {
			this.puntoventaId = puntoventaId;
		}
		
		if(StringUtils.isNotBlank(competencia)) {
			String[] vetCompetencia = competencia.split("/");
			Calendar cal = Calendar.getInstance();
			cal.set(Calendar.MONTH, Integer.valueOf(vetCompetencia[0]) - 1);
			cal.set(Calendar.YEAR, Integer.valueOf(vetCompetencia[1]));
	
			this.dataInicial = DateUtil.getDateFromString(cal.getActualMinimum(Calendar.DAY_OF_MONTH) + "/" + competencia, "dd/MM/yyyy");
			this.dataFinal = DateUtil.getDateFromString(cal.getActualMaximum(Calendar.DAY_OF_MONTH) + "/" + competencia, "dd/MM/yyyy");
		}
		
		if(status != null) {
			parametros.put("statusFiltro", status.toString());
		}
		
	}

}
