package com.mx.dla.dda.contrato.license.titulo.bos;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.result.ResultMapException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;

import com.mx.dla.dda.catalogos.daos.CatalogosDAO;
import com.mx.dla.dda.contrato.amortizacion.daos.AmortizacionDAO;
import com.mx.dla.dda.contrato.generales.dtos.ContratoDTO;
import com.mx.dla.dda.contrato.license.titulo.daos.TituloFeeChunkDAO;
import com.mx.dla.dda.contrato.license.titulo.daos.TituloFeeDAO;
import com.mx.dla.dda.contrato.license.titulo.daos.TituloFeeDmDAO;
import com.mx.dla.dda.contrato.license.titulo.rules.TituloFeeRules;
import com.mx.dla.dda.contrato.titulo.constants.TipoCargaDocumento;
import com.mx.dla.dda.contrato.titulo.daos.ListaDAO;
import com.mx.dla.dda.contrato.titulo.daos.TituloErrores;
import com.mx.dla.dda.contrato.titulo.dtos.Lista;
import com.mx.dla.dda.contrato.titulo.dtos.RespuestaCargaExcel;
import com.mx.dla.dda.contrato.titulo.dtos.Titulo;
import com.mx.dla.dda.contrato.titulo.exception.TituloException;
import com.mx.dla.dda.contrato.transaccion.exceptions.dtos.TransaccionException;
import com.mx.dla.dda.contrato.ws.cargaTitulos.ParamCargaTitulosLFDTO;
import com.mx.dla.dda.contrato.ws.cargaTitulos.ResponseCargaTitulosDTO;
import com.mx.dla.dda.excelMapper.bos.BeanPopulator;
import com.mx.dla.dda.excelMapper.bos.ExcelMapperTransform;
import com.mx.dla.dda.excelMapper.exceptions.ExcelMapperException;
import com.mx.dla.dda.restClient.bos.DLARestClient;
import com.mx.dla.dda.restClient.bos.DLARestClientFactory;
import com.mx.dla.dda.restClient.constants.DLARestServices;
import com.mx.dla.global.bos.BaseBO;

@Component
public class TituloFeeBO extends BaseBO {

	@Autowired
	private BeanPopulator beanPopulator;

	@Autowired
	private DLARestClientFactory dlaRestClientFactory;

	@Autowired
	protected ListaDAO listaDAO;

	@Autowired
	protected CatalogosDAO catalogosDAO;

	@Autowired
	protected ExcelMapperTransform excelMapperTransform;

	@Autowired
	protected TituloErrores tituloErrores;

	@Autowired
	private AmortizacionDAO amortizacionDAO;

	@Autowired
	protected TituloFeeDAO tituloDAO;

	@Autowired
	protected TituloFeeDmDAO tituloDummyDAO;

	@Autowired
	protected TituloFeeGridBO titulogridBO;

	@Autowired
	private TituloFeeChunkDAO tituloChunkDAO;

	@Autowired
	protected TituloFeeRules tituloRules;

	@Autowired
	private TituloFeeBussinesBO tituloFeeBussinesBO;

	@Value("${uri.file.titulos}")
	protected String uriErrores;

	@Value("${uri.file.carga.titulosLF}")
	protected String uriCargaTitulos;

	protected static final int CHUNK = 1000;

	public Lista init(ContratoDTO c) {
		Date a = new Date();

		Lista lista = listaDAO.buscarListaActual(c.getIdContrato(), Lista.REAL);
		if (lista == null)
			lista = this.crearListaNueva(c.getIdContrato(), c.getFechaInicio(), Lista.REAL);

		this.createMirrorEntities(c);

		lista = listaDAO.buscarListaActual(c.getIdContrato(), Lista.MIRROR);
		lista.setFechaInicioPlusOneDay(this.calculaFechaMasUnDia(lista.getFechaInicio()));

		logger.debug("Total time ms [{}]", new Date().getTime() - a.getTime());
		return lista;
	}

	public Lista crearListaNueva(Long idContrato, Date fechaInicio, String prefix) {
		Lista l = new Lista(idContrato, fechaInicio, prefix);
		listaDAO.insertarLista(l);
		return l;
	}

	public void guardarCambiosPermanente(ContratoDTO c) throws ResultMapException, SQLException, TransaccionException {
		try {
			tituloDAO.borrarTitulosPorContrato(c.getIdContrato());
			listaDAO.borrarListaPorIdContrato(c.getIdContrato(), Lista.REAL);

			tituloChunkDAO.aListaRealPorCorato(c.getIdContrato());

			List<Lista> lreal = listaDAO.buscarListas(c.getIdContrato(), Lista.REAL);
			List<Lista> lespejo = listaDAO.buscarListas(c.getIdContrato(), Lista.MIRROR);

			for (int i = 0; i < lreal.size(); i++) {
				tituloChunkDAO.aTituloRealPorLista(lespejo.get(i).getIdLista(), lreal.get(i).getIdLista());
			}
			amortizacionDAO.updateContratoProceso(c.getIdContrato(), 1);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
	}

	public void createMirrorEntities(ContratoDTO c) {
		tituloDummyDAO.borrarTitulosPorContrato(c.getIdContrato());
		listaDAO.borrarListaPorIdContrato(c.getIdContrato(), Lista.MIRROR);
		tituloChunkDAO.aListaEspejoPorCorato(c.getIdContrato());

		List<Lista> lreal = listaDAO.buscarListas(c.getIdContrato(), Lista.REAL);
		List<Lista> lespejo = listaDAO.buscarListas(c.getIdContrato(), Lista.MIRROR);

		for (int i = 0; i < lreal.size(); i++)
			tituloChunkDAO.aTituloEspejoPorLista(lreal.get(i).getIdLista(), lespejo.get(i).getIdLista());
	}

	public byte[] generateExcelReport(Titulo titulo) throws ExcelMapperException, IOException {
		logger.debug("Descargar archivo de excel [{}]", titulo);
		List<Titulo> lista = new ArrayList<Titulo>();
		try {
			lista = tituloDummyDAO.buscarTituloFiltroPaginado(titulo);

			if (lista == null || lista.isEmpty()) {
				lista = new ArrayList<Titulo>();
				lista.add(beanPopulator.initObject(Titulo.class));
			}
			// for(Titulo h : lista)
			// reporte.add(h);
			logger.debug("Descargar archivo, se han  obtenido [{}]", lista.size());
		} catch (Exception e) {
			logger.error("Error al buscar contenido de titulos");
		}
		byte[] fileExcel = excelMapperTransform.transformObjectToExcel(lista, "tituloLicense", Titulo.class);
		logger.debug("Descargar archivo, se ha generado binario.");
		return fileExcel;
	}

	public byte[] buscarArchivoDeErrores(ContratoDTO ct) throws IOException {
		File file = new File(uriErrores + ct.getIdContrato());
		logger.debug("busca archivo en [{}]", file.getAbsolutePath());
		byte[] errores = FileUtils.readFileToByteArray(file);
		return errores;
	}

	public RespuestaCargaExcel cargaMasivaDeTitulos2(Titulo titulo, ContratoDTO contrato, TipoCargaDocumento tc,
			File fileUpload, String fileName, Lista listaActual, String expediente) throws TituloException, TransaccionException {

		logger.debug("Inicio proceso remoto");
		RespuestaCargaExcel r = new RespuestaCargaExcel();
		Double costo = tituloDummyDAO.buscarTotalCostoTitulos(contrato.getIdContrato(), Lista.MIRROR);
		costo = costo != null ? costo : 0d;
		try {

			// Copia archivo en file system
			byte[] f = FileUtils.readFileToByteArray(fileUpload);
			File d = new File(uriCargaTitulos + contrato.getIdContrato() + expediente + fileName);

			d.setExecutable(true, false);
			d.setReadable(true, false);
			d.setWritable(true, false);

			logger.info("Absolute path [{}]  -  [{}]", d.getCanonicalPath(), d.getPath());
			FileUtils.writeByteArrayToFile(d, f);

			DLARestClient c = dlaRestClientFactory.getClient(DLARestServices.CARGA_TITULOS_LICENSE);
			logger.info("LLamando al Rest {}", c.getUri());
			// Arma request para rest
			ParamCargaTitulosLFDTO request = new ParamCargaTitulosLFDTO();
			request.setNombreArchivo(FilenameUtils.getName(d.getCanonicalPath()));
			request.setIdContrato(contrato.getIdContrato().toString());
			request.setIdListaActual(listaActual.getIdLista().toString());
			request.setTipoLista(TipoCargaDocumento.NUEVA.equals(tc) ? "NUEVA" : "ACTUAL");
			request.setTipoCarga(TipoCargaDocumento.ANEXAR.equals(tc) ? "AGREGAR" : "REEMPLAZAR");
			request.setExpediente(expediente);
			Calendar cal = Calendar.getInstance();
			cal.setTime(contrato.getFechaInicio());
			cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
			cal.set(Calendar.DAY_OF_MONTH, 1);
			DateFormat fecha = new SimpleDateFormat("dd/MM/yyyy");
			// request.setFechaInicioContrato(fecha.format(cal.getTime()));
			request.setFechaInicioListaNueva(
					titulo.getFechaInicio() != null ? fecha.format(titulo.getFechaInicio()) : "");

			logger.debug("Request carga titulos [{}]", request);
			ResponseCargaTitulosDTO response = c.get(request, ResponseCargaTitulosDTO.class);
			logger.debug("Response carga titulos [{}]", response);

			if (!response.isResultado()) {
				throw new TituloException(response.getMensaje());
			}

			r.setResponse(response);

			// genera archivo de errores si hay errores
			if (!response.getDatosCarga().getTOTAL_ERROR().equals("0")) {
				logger.debug("Inicio Creación Excel.");
				List<Titulo> a = new ArrayList<Titulo>();
				Date d1 = new Date();
				a = tituloErrores.buscarErroresTitulo(Long.parseLong(response.getDatosCarga().getID_CARGA()));
				logger.debug("Tiempo de busqueda [{}] total Errors[{}]", new Date().getTime() - d1.getTime(), a.size());
				d1 = new Date();
				byte[] fileExcel = excelMapperTransform.transformObjectToExcel(a, "tituloLicense", Titulo.class);
				// byte[] fileExcel = generaExcel(a);
				logger.debug("Tiempo de transformacion [{}] ", new Date().getTime() - d1.getTime());
				File destFile = new File(uriErrores + contrato.getIdContrato());
				FileUtils.writeByteArrayToFile(destFile, fileExcel);
				d1 = new Date();
				logger.debug("Tiempo de escritura [{}] ", new Date().getTime() - d1.getTime());
				logger.debug("Fin Creación Excel.");
			}

		} catch (ExcelMapperException e) {
			logger.error("Errores [{}]", e.getSErrors());
			logger.error("Error al leer el archivo", e);

		} catch (IOException e) {
			logger.debug("Error ", e);
			throw new TituloException("Ocurrio un error al subir el documento");
		}

		logger.debug("Inicio Busqueda.");
		Lista listaA = listaDAO.buscarListaActual(contrato.getIdContrato(), Lista.MIRROR);
		logger.debug("Fin Busqueda.");

		// si esnueva no deberia tener presupuesto previo
		if (!TipoCargaDocumento.ANEXAR.equals(tc)) {
			costo = 0d;
		}

		tituloFeeBussinesBO.actualizaCostoTituloExcel(contrato.getIdContrato());

		logger.debug("Inicio Busqueda Costo.");
		Double costoFinal = tituloDummyDAO.buscarTotalCostoTitulos(contrato.getIdContrato(), Lista.MIRROR);
		costoFinal = costoFinal != null ? costoFinal : 0d;
		logger.debug("Costo anterior [{}] costo final[{}]", costo, costoFinal);

		r.setMonto(costoFinal - costo);
		r.setLista(listaA);
		return r;
	}

	public List<TipoCargaDocumento> buscaTipoCargaDocumento(ContratoDTO ct) {
		List<TipoCargaDocumento> r = new ArrayList<TipoCargaDocumento>();
		r.add(TipoCargaDocumento.ANEXAR);
		r.add(TipoCargaDocumento.REEMPLAZAR);
		List<Lista> l = listaDAO.buscarListasPorContratoCreacion(ct.getIdContrato(), Lista.MIRROR);
		if (l.size() == 0) {
			r.add(TipoCargaDocumento.NUEVA);
		}
		return r;
	}

	public Date calculaFechaMasUnDia(Date in) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(in);
		cal.set(Calendar.DAY_OF_YEAR, cal.get(Calendar.DAY_OF_YEAR) + 1);
		return cal.getTime();
	}

	public String getUriCarga() {
		return uriCargaTitulos;
	}

	/*
	 * public void guardaLoteReal(List<Titulo> titulos) { int total =
	 * titulos.size(); int step = 0; int end = (total / CHUNK) +1;
	 * 
	 * logger.debug("Total [{}]",total); logger.debug("End [{}]",end); if(total !=
	 * 0){ do { int from = step * CHUNK; int to = from + CHUNK > total ? from +
	 * (total % CHUNK) : from + CHUNK; step ++;
	 * logger.debug(" step[{}] from [{}]   ,  to [{}]",new Object[] {step, from,
	 * to-1});
	 * 
	 * List<Titulo> p = titulos.subList(from, to); tituloChunkDAO.aReal(p);
	 * 
	 * }while(step != end); } }
	 * 
	 * public void guardaLoteEspejo(List<Titulo> titulos) { int total =
	 * titulos.size(); int step = 0; int end = (total / CHUNK) +1;
	 * 
	 * logger.debug("Total [{}]",total); logger.debug("End [{}]",end); if(total !=
	 * 0){ do { int from = step * CHUNK; int to = from + CHUNK > total ? from +
	 * (total % CHUNK) : from + CHUNK; step ++;
	 * logger.debug(" step[{}] from [{}]   ,  to [{}]",new Object[] {step, from,
	 * to-1});
	 * 
	 * List<Titulo> p = titulos.subList(from, to); tituloChunkDAO.aEspejo(p);
	 * 
	 * }while(step != end); } }
	 * 
	 */

}
