package com.rjconsultores.ventaboletos.web.gui.controladores.esquemaoperacional;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.zkoss.util.resource.Labels;
import org.zkoss.zhtml.Messagebox;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Filedownload;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Radio;

import com.rjconsultores.ventaboletos.entidad.Empresa;
import com.rjconsultores.ventaboletos.exception.BusinessException;
import com.rjconsultores.ventaboletos.service.EmpresaService;
import com.rjconsultores.ventaboletos.web.utilerias.MyGenericForwardComposer;
import com.rjconsultores.ventaboletos.web.utilerias.NamedParameterStatement;

@Controller("geracaoArquivoEMTUController")
@Scope("prototype")
public class GeracaoArquivoEMTUController extends MyGenericForwardComposer {

	private static final long serialVersionUID = 1L;

	private Datebox datReferencia;
	private Combobox cmbEmpresa;
	private Intbox txtNumSequencia;
	private Radio rdViagem;

	private List<Empresa> lsEmpresa;

	@Autowired
	private EmpresaService empresaService;
	@Autowired
	private DataSource dataSource;

	@Override
	public void doAfterCompose(Component comp) throws Exception {
		lsEmpresa = empresaService.obtenerTodos();

		super.doAfterCompose(comp);
	}

	public void onClick$btnGerarArquivo(Event ev) throws Exception {
		cmbEmpresa.getValue();
		txtNumSequencia.getValue();
		datReferencia.getValue();

		Empresa empresa = (Empresa) cmbEmpresa.getSelectedItem().getValue();

		if (empresa.getCodigoEmtu() == null || empresa.getCodigoEmtu() == 0) {
			Messagebox.show(Labels.getLabel("geracaoArquivoEMTUController.MSG.empresaSemCodigoEMTU"),
					Labels.getLabel("geracaoArquivoEMTU.window.title"),
					Messagebox.OK, Messagebox.INFORMATION);
			return;
		}


		baixarArquivos(montarNomeArquivo(empresa.getCodigoEmtu(), txtNumSequencia.getValue()));
		salvarNumSequencia(txtNumSequencia.getValue(), empresa);
	}

	private void baixarArquivos(String nomeArquivo) throws Exception {
		
		List<ItemViagem> itensViagem = new ArrayList<ItemViagem>();
		List<ItemReceita> itensReceita = new ArrayList<ItemReceita>();

		if (rdViagem.isChecked()) {
			itensViagem = getItensViagem();
		} else {
			itensReceita = getItensReceita();
		}

		if (itensViagem.isEmpty() && itensReceita.isEmpty()) {
			Messagebox.show(Labels.getLabel("geracaoArquivoEMTUController.MSG.dadosVazios"),
					Labels.getLabel("geracaoArquivoEMTU.window.title"),
					Messagebox.OK, Messagebox.INFORMATION);
			return;
		}
		
		FileOutputStream fos = new FileOutputStream(nomeArquivo);
		fos.write(rdViagem.isChecked() ? getDadosArquivoViagens(itensViagem).getBytes() : getDadosArquivoReceita(itensReceita).getBytes());
		fos.close();
		InputStream is = new FileInputStream(nomeArquivo);
		Filedownload.save(is, "application/txt", nomeArquivo);
	}

	private String getDadosArquivoViagens(List<ItemViagem> itensViagem) {
		StringBuilder dados = new StringBuilder();

		dados.append(getHeader("01"));
		dados.append(getViagens(itensViagem));

		return dados.toString();
	}

	private String getViagens(List<ItemViagem> itensViagem) {
		StringBuilder dados = new StringBuilder();

		Integer numeroRegistro = 2;
		SimpleDateFormat formatoSemHora = new SimpleDateFormat("ddMMyyyy");
		SimpleDateFormat formatoHora = new SimpleDateFormat("HHmm");

		for (ItemViagem item : itensViagem) {
			dados.append("\n")
					.append(completeZerosToLeft(numeroRegistro.toString(), 6))
					.append("V")
					.append("000000") // prefixo do veiculo
					.append(completeZerosToLeft(item.passageiros.toString(), 6))
					.append(completeZerosToLeft(item.viagens.toString(), 4))
					.append(StringUtils.rightPad(item.linha, 6))
					.append(item.sentido == 0 ? "2" : "1")
					.append(completeZerosToLeft(item.km.toString(), 6))
					.append(formatoSemHora.format(item.dataReferencia))
					.append("002") // tipo do veiculo
					.append(formatoHora.format(item.dataSaida))
					.append(formatoHora.format(item.dataChegada))
					.append("0000000");
			numeroRegistro++;
		}

		return dados.toString();
	}

	private String getDadosArquivoReceita(List<ItemReceita> itensReceita) {
		StringBuilder dados = new StringBuilder();

		dados.append(getHeader("03"));
		dados.append(getReceitas(itensReceita));

		return dados.toString();
	}

	private String getReceitas(List<ItemReceita> itensReceita) {
		StringBuilder dados = new StringBuilder();

		Integer numeroRegistro = 2;
		SimpleDateFormat formatoSemHora = new SimpleDateFormat("ddMMyyyy");
		NumberFormat formatter = new DecimalFormat("#0.00");

		for (ItemReceita item : itensReceita) {
			dados.append("\n")
					.append(completeZerosToLeft(numeroRegistro.toString(), 6))
					.append("R")
					.append(completeZerosToLeft(item.linha, 6))
					.append("100") // Tipo Passagem
					.append("7") // Modalidade Pagamento
					.append(completeZerosToLeft(item.passagensVendidas.toString(), 7))
					.append(completeZerosToLeft(formatter.format(item.valor.doubleValue()).replace(",", ""), 8))
					.append(formatoSemHora.format(item.dataReferencia))
					.append(item.sentido == 0 ? "2" : "1")
					.append(item.tipoReceita == 0 ? "01" : "02")
					.append("      ") // Linha integração
					.append("      ")
					.append("0000000"); // id da linha caso seja de 7 digitos
			numeroRegistro++;
		}

		return dados.toString();
	}

	private List<ItemReceita> getItensReceita() {
		List<ItemReceita> result = new ArrayList<ItemReceita>();

		String sql = getSqlReceita();

		try {
			NamedParameterStatement stmt = new NamedParameterStatement(dataSource.getConnection(), sql);
			ResultSet rset = stmt.executeQuery();

			while (rset.next()) {
				ItemReceita item = new ItemReceita();
				item.linha = rset.getString("linha");
				item.passagensVendidas = rset.getInt("passagensVendidas");
				item.valor = rset.getBigDecimal("valor");
				item.dataReferencia = rset.getTimestamp("dataReferencia");
				item.sentido = rset.getInt("sentido");
				item.tipoReceita = rset.getInt("tipoReceita");
				result.add(item);
			}

		} catch (SQLException e) {
			e.printStackTrace();
		}

		return result;
	}

	private String getSqlReceita() {

		StringBuilder sql = new StringBuilder();

		SimpleDateFormat formatSemHora = new SimpleDateFormat("dd/MM/yyyy");
		String formatToDate = ("'dd/MM/yyyy hh24:mi:ss'");

		sql.append("SELECT DISTINCT ");
		sql.append("ruta.NUMRUTA as linha, ");
		sql.append("COUNT(boleto.BOLETO_ID) as passagensVendidas, ");
		sql.append("ruta.INDSENTIDOIDA as sentido, ");
		sql.append("(CASE WHEN origem.CIUDAD_ID = destino.CIUDAD_ID THEN 0 ELSE 1 END) as tipoReceita, ");
		sql.append("SUM( coalesce(boleto.PRECIOPAGADO,0) + coalesce(boleto.IMPORTEOUTROS,0) ");
		sql.append("+ coalesce(boleto.IMPORTEPEDAGIO,0) + coalesce(boleto.IMPORTESEGURO,0)  ");
		sql.append("+ coalesce(boleto.IMPORTETAXAEMBARQUE,0)) as valor, ");
		sql.append("TRUNC(boleto.FECHORVENTA) AS dataReferencia ");

		sql.append("FROM BOLETO boleto ");
		sql.append("INNER JOIN RUTA ruta ON ruta.RUTA_ID = boleto.RUTA_ID ");
		sql.append("INNER JOIN PARADA origem ON origem.PARADA_ID = boleto.ORIGEN_ID ");
		sql.append("INNER JOIN PARADA destino ON destino.PARADA_ID = boleto.DESTINO_ID ");

		sql.append("AND boleto.ACTIVO         = 1 ");
		sql.append("AND boleto.INDCANCELACION = 0 ");
		sql.append("AND boleto.INDREIMPRESION = 0 ");
		sql.append("AND boleto.INDSTATUSOPERACION = 'F' ");
		sql.append("AND boleto.FECHORVENTA BETWEEN TO_DATE('" + formatSemHora.format(datReferencia.getValue()) + " 00:00:00', " + formatToDate + ") "
				+ "AND TO_DATE('" + formatSemHora.format(datReferencia.getValue()) + " 23:59:59', " + formatToDate + ") ");

		sql.append("GROUP BY ruta.NUMRUTA, ");
		sql.append("ruta.INDSENTIDOIDA, ");
		sql.append("(CASE WHEN origem.CIUDAD_ID = destino.CIUDAD_ID THEN 0 ELSE 1 END), ");
		sql.append("TRUNC(boleto.FECHORVENTA) ");

		return sql.toString();
	}

	private String getHeader(String codigoDemonstracao) {
		StringBuilder header = new StringBuilder();

		Empresa empresa = (Empresa) cmbEmpresa.getSelectedItem().getValue();

		header.append("000001")
				.append("H")
				.append("1")
				.append(completeZerosToLeft(txtNumSequencia.getText(), 3))
				.append(codigoDemonstracao)
				.append(String.format("%-20.20s", empresa.getNombempresa()))
				.append(new SimpleDateFormat("ddMMyyyy").format(datReferencia.getValue()))
				.append(completeZerosToLeft(empresa.getCodigoEmtu().toString(), 5))
				.append("         ")
				.append("0000256");

		return header.toString();
	}

	private String montarNomeArquivo(Integer codigoEmtu, Integer numSequencia) {
		return "RM" + completeZerosToLeft(codigoEmtu.toString(), 5) + "." + completeZerosToLeft(numSequencia.toString(), 3) + ".txt";
	}

	private String completeZerosToLeft(String value, int size) {
		String result = value;
		while (result.length() < size) {
			result = '0' + result;
		}
		return result;
	}

	private void salvarNumSequencia(int num, Empresa empresa) throws BusinessException {
		if (num == 999) {
			num = 0;
		} else {
			num++;
		}
		empresa = empresaService.obtenerID(empresa.getEmpresaId());
		empresa.setSequenciaEmtu(num);
		empresaService.suscribirActualizacion(empresa);
		txtNumSequencia.setValue(num);
		cmbEmpresa.setModel(new ListModelList(empresaService.obtenerTodos()));
	}

	public void onChange$cmbEmpresa(Event ev) {
		Empresa empresa = (Empresa) cmbEmpresa.getSelectedItem().getValue();
		if (empresa != null) {
			txtNumSequencia.setValue(empresa.getSequenciaEmtu() == null ? 0 : empresa.getSequenciaEmtu());
		}
	}

	public List<Empresa> getLsEmpresa() {
		return lsEmpresa;
	}

	public void setLsEmpresa(List<Empresa> lsEmpresa) {
		this.lsEmpresa = lsEmpresa;
	}

	private List<ItemViagem> getItensViagem() {
		List<ItemViagem> result = new ArrayList<ItemViagem>();

		String sql = getSqlViagem();

		try {
			NamedParameterStatement stmt = new NamedParameterStatement(dataSource.getConnection(), sql);
			ResultSet rset = stmt.executeQuery();

			while (rset.next()) {
				ItemViagem item = new ItemViagem();
				item.dataChegada = rset.getTimestamp("dataChegada");
				item.dataReferencia = rset.getTimestamp("dataReferencia");
				item.dataSaida = rset.getTimestamp("dataSaida");
				item.linha = rset.getString("linha");
				item.passageiros = rset.getInt("passageiros");
				item.prefixo = rset.getString("prefixo");
				item.sentido = rset.getInt("sentido");
				item.km = rset.getInt("km");
				item.viagens = rset.getInt("viagens");

				result.add(item);
			}

		} catch (SQLException e) {
			e.printStackTrace();
		}

		return result;
	}

	private String getSqlViagem() {
		StringBuilder sql = new StringBuilder();

		SimpleDateFormat formatSemHora = new SimpleDateFormat("dd/MM/yyyy");
		String formatToDate = ("'dd/MM/yyyy hh24:mi:ss'");

		sql.append("SELECT DISTINCT ");
		sql.append("ruta.PREFIXO AS prefixo, ");
		sql.append("(SELECT COUNT(*) FROM BOLETO b ");
		sql.append("WHERE b.CORRIDA_ID = corrida.CORRIDA_ID ");
		sql.append("AND b.FECCORRIDA = corrida.FECCORRIDA ");
		sql.append("AND b.ACTIVO         = 1 ");
		sql.append("AND b.INDCANCELACION = 0 ");
		sql.append("AND b.INDREIMPRESION = 0 ");
		sql.append("AND b.INDSTATUSOPERACION = 'F') AS passageiros, ");
		sql.append("(SELECT COUNT(DISTINCT c.CORRIDA_ID) ");
		sql.append("FROM CORRIDA c ");
		sql.append("INNER JOIN RUTA             l ON (l.RUTA_ID            = c.RUTA_ID            AND l.ACTIVO = 1) ");
		sql.append("WHERE c.FECCORRIDA   = corrida.FECCORRIDA ");
		sql.append("AND l.RUTA_ID        = ruta.RUTA_ID ");
		sql.append("AND c.AUTOBUS_ID     = corrida.AUTOBUS_ID ");
		sql.append("AND c.ACTIVO         = 1) AS viagens, ");
		sql.append("ruta.NUMRUTA AS linha, ");
		sql.append("ruta.INDSENTIDOIDA AS sentido, ");
		sql.append("(SELECT TRUNC(SUM(t.CANTKMREAL)) FROM  CORRIDA_TRAMO ct ");
		sql.append("INNER JOIN TRAMO t ON t.TRAMO_ID = ct.TRAMO_ID ");
		sql.append("WHERE  corrida.CORRIDA_ID = ct.CORRIDA_ID ");
		sql.append("AND ct.FECCORRIDA = corrida.FECCORRIDA) AS km, ");
		sql.append("corrida.FECCORRIDA AS dataReferencia, ");
		sql.append("corrida.FECHORSALIDA AS dataSaida, ");
		sql.append("(SELECT temp.FECHORLLEGADA FROM ( ");
		sql.append("SELECT ct.FECHORLLEGADA, ct.CORRIDA_ID, ct.FECCORRIDA ");
		sql.append("FROM CORRIDA_TRAMO ct ");
		sql.append("WHERE ct.ACTIVO = 1 ");
		sql.append("ORDER BY ct.NUMSECUENCIA DESC) temp ");
		sql.append("WHERE temp.CORRIDA_ID = corrida.CORRIDA_ID ");
		sql.append("AND temp.FECCORRIDA = corrida.FECCORRIDA AND ROWNUM = 1) as dataChegada ");

		sql.append("FROM CORRIDA corrida ");
		sql.append("INNER JOIN RUTA                 ruta ON (ruta.RUTA_ID       = corrida.RUTA_ID    AND ruta.ACTIVO        = 1) ");
		sql.append("INNER JOIN BOLETO 			  boleto ON (boleto.CORRIDA_ID  = corrida.CORRIDA_ID AND corrida.FECCORRIDA = boleto.FECCORRIDA) ");
		sql.append("INNER JOIN AUTOBUS 			 autobus ON (autobus.AUTOBUS_ID = corrida.AUTOBUS_ID) ");
		sql.append("WHERE corrida.ACTIVO = 1 ");
		sql.append("AND boleto.ACTIVO = 1 ");
		sql.append("AND corrida.FECCORRIDA BETWEEN TO_DATE('" + formatSemHora.format(datReferencia.getValue()) + " 00:00:00', " + formatToDate + ") "
				+ "AND TO_DATE('" + formatSemHora.format(datReferencia.getValue()) + " 23:59:59', " + formatToDate + ") ");

		return sql.toString();
	}

	class ItemViagem {
		public String prefixo;
		public Integer passageiros;
		public Integer viagens;
		public String linha;
		public Integer sentido;
		public Integer km;
		public Timestamp dataReferencia;
		public Timestamp dataSaida;
		public Timestamp dataChegada;
	}

	class ItemReceita {
		public String linha;
		public Integer passagensVendidas;
		public BigDecimal valor;
		public Timestamp dataReferencia;
		public Integer sentido;
		public Integer tipoReceita;
	}

}
