package com.rjconsultores.ventaboletos.service.impl;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.SQLException;

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.Service;

import com.rjconsultores.ventaboletos.ArquivoRetornoItem;
import com.rjconsultores.ventaboletos.blocos.CabecalhoRetorno;
import com.rjconsultores.ventaboletos.blocos.DetalheRetorno;
import com.rjconsultores.ventaboletos.blocos.itau.DetalheRetornoItau;
import com.rjconsultores.ventaboletos.blocos.safra.DetalheRetornoSafra;
import com.rjconsultores.ventaboletos.dao.RemessaCNABBancosDAO;
import com.rjconsultores.ventaboletos.entidad.Empresa;
import com.rjconsultores.ventaboletos.entidad.FechamentoBoleto;
import com.rjconsultores.ventaboletos.entidad.FechamentoParamgeral;
import com.rjconsultores.ventaboletos.enuns.BancoLayout;
import com.rjconsultores.ventaboletos.service.FechamentoParamgeralService;
import com.rjconsultores.ventaboletos.service.ImportacaoRetornoBancarioService;
import com.rjconsultores.ventaboletos.utilerias.UsuarioLogado;
import com.rjconsultores.ventaboletos.utils.NossoNumeroUtils;

@Service("importacaoRetornoBancarioService")
public class ImportacaoRetornoBancarioServiceImpl implements ImportacaoRetornoBancarioService {

	private static final String LIQUIDACAO_NORMAL = "06";
	private static final String LIQUIDACAO_EM_CARTORIO = "08";

	@Autowired
	private RemessaCNABBancosDAO remessaCNABBancosDAO;
	
	@Autowired
	private FechamentoParamgeralService fechamentoParamgeralService;
	          

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

	@Override
	public String lerArquivo(ByteArrayInputStream bais, Empresa empresa) {

		StringBuilder resultado = new StringBuilder();
		StringBuilder detalhado = new StringBuilder();

		Integer atualizados = 0;
		Integer erros = 0;

		try {
			BancoLayout banco = BancoLayout.getInstanceByCodBanco(remessaCNABBancosDAO.findBanco(empresa));
			ArquivoRetornoItem retornoBancario = processaRetornoBancario(bais, banco);

			if (retornoBancario == null) {
				resultado.append("O banco cadastrado para a empresa não tem Implementação de retorno bancário");
				return resultado.toString();
			}

			if (!banco.getCodBanco().equals(retornoBancario.getCabecalhoRetorno().getCodigoBanco())) {
				resultado.append("O banco cadastrado para a empresa não é o mesmo do arquivo selecionado");
				return resultado.toString();
			}

			for (DetalheRetorno detalhe : retornoBancario.getTitulos()) {
				Integer usuarioId = UsuarioLogado.getUsuarioLogado().getUsuarioId();

				try {
					if (!detalhe.getCodigoOcorrencia().equals(LIQUIDACAO_NORMAL)
							&& !detalhe.getCodigoOcorrencia().equals(LIQUIDACAO_EM_CARTORIO)) {
						erros++;
						detalhado.append("Registro sem ocorrencia de quitação: ").append(detalhe.getNossoNumero())
								.append(".\n");
					} else
					if (salvarRetornoBancario(detalhe, empresa.getEmpresaId(), usuarioId)) {
						detalhado.append("Quitado:").append(detalhe.getNossoNumero()).append(".\n");
						atualizados++;
					} else {
						erros++;
						detalhado.append("Nao Quitado: ").append(detalhe.getNossoNumero()).append(".\n");
					}
				} catch (SQLException se) {
					detalhado.append("Ocorreu um erro no banco de dados: ").append(detalhe.getNossoNumero())
							.append(".\n");
					log.error(se);
					erros++;
				} catch (RuntimeException re) {
					detalhado.append(re.getMessage()).append(".\n");
					log.error(re);
					erros++;
				}
			}

			resultado.append("Arquivo Processado ");
			if (erros > 0) {
				resultado.append("com ressalvas.\n");
			} else {
				resultado.append("com sucesso. \n");
			}
			resultado.append("Quitados ").append(atualizados).append(".\n");
			resultado.append("Nao Quitados ").append(erros).append(".\n");
			resultado.append(detalhado);
			return resultado.toString();

		} catch (IOException ioe) {
			resultado.append("Ocorreu um erro ao processar o arquivo enviado");
			log.error(ioe);
			return resultado.toString();
		} catch (RuntimeException re) {
			resultado.append(re.getMessage());
			log.error(re);
			return resultado.toString();
		} catch (Exception e) {
			resultado.append("Ocorreu um erro ao processar o arquivo enviado");
			log.error(e);
			return resultado.toString();
		}
	}

	private ArquivoRetornoItem processaRetornoBancario(ByteArrayInputStream bais, BancoLayout banco)
			throws IOException {

		if (BancoLayout.ITAU_400_Envio.equals(banco)) {
			return geraRetornoBancarioItau(bais, banco);
		} else if (BancoLayout.SAFRA_400_Envio.equals(banco)) {
			return geraRetornoBancarioSafra(bais);
		} else {
			return null;
		}
	}

	private ArquivoRetornoItem geraRetornoBancarioItau(ByteArrayInputStream bais, BancoLayout banco)
			throws IOException {
		String linha = null;
		ArquivoRetornoItem arquivo = new ArquivoRetornoItem();
		CabecalhoRetorno cabecalho = new CabecalhoRetorno();

		BufferedReader leitor = new BufferedReader(new InputStreamReader(bais));

		while ((linha = leitor.readLine()) != null) {

			if (linha.startsWith("0")) { // cabecalho
				cabecalho.setCodigoBanco(linha.substring(76, 79));
				continue;
			}

			if (linha.startsWith("1")) { // detalhe
				DetalheRetornoItau detalhe = new DetalheRetornoItau();
				detalhe.setCodigoOcorrencia(linha.substring(108, 110));
				detalhe.setValorJuros(new BigDecimal(linha.substring(266, 279)).divide(new BigDecimal(100)));
				detalhe.setNossoNumero(linha.substring(85, 93) + "-" + linha.substring(93, 94));
				detalhe.setDataBaixa(linha.substring(110, 116));
				arquivo.addTitulo(detalhe);
				continue;
			}

			if (linha.startsWith("9")) { // rodape

			}
		}

		leitor.close();

		arquivo.setCabecalhoRetorno(cabecalho);

		return arquivo;
	}

	private ArquivoRetornoItem geraRetornoBancarioSafra(ByteArrayInputStream bais) throws IOException {
		String linha = null;
		ArquivoRetornoItem arquivo = new ArquivoRetornoItem();
		CabecalhoRetorno cabecalho = new CabecalhoRetorno();

		BufferedReader leitor = new BufferedReader(new InputStreamReader(bais));

		while ((linha = leitor.readLine()) != null) {

			if (linha.startsWith("0")) { // cabecalho
				cabecalho.setCodigoBanco(linha.substring(76, 79));
				continue;
			}

			if (linha.startsWith("1")) { // detalhe
				DetalheRetornoSafra detalhe = new DetalheRetornoSafra();
				detalhe.setIdBoletoFechamento(Integer.valueOf(linha.substring(116, 126)));
				detalhe.setCodigoOcorrencia(linha.substring(108, 110));
				detalhe.setValorJuros(new BigDecimal(linha.substring(267, 279)).divide(new BigDecimal(100)));
				detalhe.setNossoNumero(linha.substring(61, 70) + "-" + linha.substring(70, 71));
				detalhe.setDataBaixa(linha.substring(295, 301));
				arquivo.addTitulo(detalhe);
				continue;
			}

			if (linha.startsWith("9")) { // rodape

			}
		}

		leitor.close();

		arquivo.setCabecalhoRetorno(cabecalho);

		return arquivo;
	}

	@Override
	public boolean salvarRetornoBancario(DetalheRetorno detalhe, Integer empresaId, Integer usuarioId)
			throws SQLException {

		if (detalhe instanceof DetalheRetornoSafra) {
			return salvarRetornoBancario(detalhe, usuarioId);
		}

		FechamentoBoleto boleto = remessaCNABBancosDAO.obtenerFechamentoBoletoPorNossoNumero(detalhe.getNossoNumero(),
				empresaId);

		//18/09/2024 Apos um ano e meio excluir este trecho
		if (boleto == null) { 
			if (detalhe instanceof DetalheRetornoItau) { 
				return validaDacItau(detalhe, empresaId, usuarioId);
			} else {
				throw new RuntimeException(
						"Fechamento não encontrado para a empresa com o nosso numero: " + detalhe.getNossoNumero());
			}
		}

		if (boleto.getIndBoletoQuitado() != null && boleto.getIndBoletoQuitado()) {
			throw new RuntimeException(
					"Boleto já quitado para a empresa com o nosso numero: " + detalhe.getNossoNumero());
		}

		return remessaCNABBancosDAO.quitarFechamentoBoleto(boleto.getFechamentoboletoId(), usuarioId, detalhe);
	}

	@Override
	public boolean salvarRetornoBancario(DetalheRetorno detalhe, Integer usuarioId) throws SQLException {
		FechamentoBoleto boleto = remessaCNABBancosDAO.obtenerFechamentoBoletoPorId(detalhe.getIdBoletoFechamento());

		if (boleto == null) {
			throw new RuntimeException("Fechamento não encontrado com o ID: " + detalhe.getIdBoletoFechamento());
		}

		if (boleto.getNossonumero() != null && !boleto.getNossonumero().equals(detalhe.getNossoNumero())) {
			throw new RuntimeException(
					"Fechamento encontrado o nosso numero inconsistente: " + detalhe.getNossoNumero());
		}

		if (boleto.getIndBoletoQuitado() != null && boleto.getIndBoletoQuitado()) {
			throw new RuntimeException(
					"Boleto já quitado para a empresa com o nosso numero: " + detalhe.getNossoNumero());
		}

		return remessaCNABBancosDAO.quitarFechamentoBoleto(detalhe, usuarioId);

	}

	//18/09/2024 Apos um ano e meio excluir este metodo
	public boolean validaDacItau(DetalheRetorno detalhe, Integer empresaId, Integer usuarioId) throws SQLException {	
		
		
		FechamentoParamgeral dadosContaItau = fechamentoParamgeralService.buscaParametrosPorEmpresasBanco(empresaId, "341");
		BigInteger nossoNumero = new BigInteger(detalhe.getNossoNumero().substring(0, 8));
		if (dadosContaItau != null) {
			
			String dacItauAntigo = dacItauAntigo(
					Integer.valueOf(dadosContaItau.getBoletoBancoAgencia()), 
					Integer.valueOf(dadosContaItau.getBoletoBancoConta()),
					Integer.valueOf(dadosContaItau.getBoletoBancoCarteira()),
					nossoNumero);
			
			detalhe.setNossoNumero(detalhe.getNossoNumero().substring(0, 8) + "-" + dacItauAntigo);
			
			FechamentoBoleto boleto = remessaCNABBancosDAO.obtenerFechamentoBoletoPorNossoNumero(detalhe.getNossoNumero(),
					empresaId);

			if (boleto == null) {
				throw new RuntimeException("Fechamento não encontrado para a empresa com o nosso numero: " + detalhe.getNossoNumero());
			}

			if (boleto.getIndBoletoQuitado() != null && boleto.getIndBoletoQuitado()) {
				throw new RuntimeException(
						"Boleto já quitado para a empresa com o nosso numero: " + detalhe.getNossoNumero());
			}
			return remessaCNABBancosDAO.quitarFechamentoBoleto(boleto.getFechamentoboletoId(), usuarioId, detalhe);
		}else 
			throw new RuntimeException("Dados da conta não encontrados : " + detalhe.getIdBoletoFechamento());
	}
	
	//18/09/2024 Apos um ano e meio excluir este metodo
	public static String dacItauAntigo(Integer codAgencia, Integer numConta, Integer numCarteira, BigInteger nossoNumero){
		
		int multiplicadores[] = {2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1};
		int multiplicandos[] = new int[20]; 
		
		String agencia = StringUtils.right("0000" + codAgencia, 4);
		String conta = StringUtils.right("00000" + numConta, 5);
		String carteira = StringUtils.right("000" + numCarteira, 3);
		String nNumero = StringUtils.right("00000000" + nossoNumero, 8);
		
		String base = agencia + conta + carteira + nNumero;
		
		for (int i = 0; i < multiplicandos.length; i++) {
			
			String d = base.charAt(i) + "";
			
			multiplicandos[i] = Integer.valueOf(d) * multiplicadores[i];
		}
		
		int  dac = 0;
		
		for(Integer v : multiplicandos){
			
			if(v < 10){
				dac += v;
			}else{
				dac += 1 + (v - 10);
			}
		}
		
		dac = 10 - (dac % 10); 
		
		return dac == 10 ? "0" : dac+"";
	}
}
