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

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.mx.dla.dda.catalogos.daos.CatalogosDAO;
import com.mx.dla.dda.catalogos.dtos.CatalogoDTO;
import com.mx.dla.dda.contrato.amortizacion.daos.AmortizacionDAO;
import com.mx.dla.dda.contrato.generales.dtos.ContratoDTO;
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.TituloChunkDAO;
import com.mx.dla.dda.contrato.titulo.daos.TituloDAO;
import com.mx.dla.dda.contrato.titulo.daos.TituloDmDAO;
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.dtos.TitulosContratoSap;
import com.mx.dla.dda.contrato.titulo.exception.TituloException;
import com.mx.dla.dda.contrato.titulo.rules.TituloRules;
import com.mx.dla.dda.contrato.transaccion.exceptions.dtos.TransaccionException;
import com.mx.dla.dda.contrato.ws.cargaTitulos.CargaPrincipalDTO;
import com.mx.dla.dda.contrato.ws.cargaTitulos.ParamCargaCostosDTO;
import com.mx.dla.dda.contrato.ws.cargaTitulos.ParamCargaTitulosDTO;
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 TituloBO extends BaseBO {

	@Autowired
	protected TituloDAO tituloDAO;
	@Autowired
	protected TituloDmDAO tituloDummyDAO;
	@Autowired
	protected ListaDAO listaDAO;
	@Autowired
	protected CatalogosDAO catalogosDAO;
	@Autowired
	protected ExcelMapperTransform excelMapperTransform;
	@Autowired
	protected TituloGridBO titulogridBO;
	@Autowired
	private TituloChunkDAO tituloChunkDAO;
	@Autowired
	protected TituloRules tituloRules;
	@Autowired
	protected TituloErrores tituloErrores;
	@Autowired
	private AmortizacionDAO amortizacionDAO;
	@Autowired
	private BeanPopulator beanPopulator;
	@Autowired
	protected TitulosBussinesBO titulosBussinesBO;

	@Autowired
	private DLARestClientFactory dlaRestClientFactory;

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

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

	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 = crearListaNueva(c.getIdContrato(), c.getFechaInicio(), Lista.REAL);

		createMirrorEntities(c);
		lista = listaDAO.buscarListaActual(c.getIdContrato(), Lista.MIRROR);
		lista.setFechaInicioPlusOneDay(calculaFechaMasUnDia(lista.getFechaInicio()));
		logger.debug("Total time ms [{}]", new Date().getTime() - a.getTime());
		return lista;
	}

	public List<TitulosContratoSap> recuperarTitulos(Long idContrato) {
		return tituloDAO.recuperarTitulosContrato(idContrato);
	}

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

	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 void guardarCambiosPermanente(ContratoDTO c) throws TituloException {
		String valido = titulosBussinesBO.procesoActualizacionTitulosEnmienda(c.getIdContrato(), "M");

		if (valido != null) {
			throw new TituloException(
					"Los titulos [" + valido + "] no pueden tener costo menor a su valor neto contable.");
		} else {
			titulosBussinesBO.actualizarSapnumTraslados(c.getIdContrato());

			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);
		}
	}

	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));
			}
			logger.debug("Descargar archivo, se han  obtenido [{}]", lista.size());
		} catch (Exception e) {
			logger.error("Error al buscar contenido de titulos::: " + e);
		}
		byte[] fileExcel = excelMapperTransform.transformObjectToExcel(lista, "titulo", 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 actualizarCostos(ContratoDTO contrato, File fileUpload, String fileName, Titulo tit) {
		List<ParamCargaCostosDTO> precios = new ArrayList<>();
		byte[] f;
		File d = null;
		try {
			f = FileUtils.readFileToByteArray(fileUpload);
			d = new File(fileName);

			d.setExecutable(true, false);
			d.setReadable(true, false);
			d.setWritable(true, false);
			logger.debug("Absolute path [{}]  -  [{}]", d.getCanonicalPath(), d.getPath());
			FileUtils.writeByteArrayToFile(d, f);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		if (d.getName().endsWith(".xls")) {
			FileInputStream file;
			HSSFWorkbook workbook = null;
			try {
				file = new FileInputStream(d);
				workbook = new HSSFWorkbook(file);
			} catch (IOException e) {
				e.printStackTrace();
			}
			HSSFSheet sheet = workbook.getSheetAt(0);
			int lnuUltimaFila = sheet.getPhysicalNumberOfRows();
			logger.debug("leerCargaTitulosXLS[" + this.getClass().getSimpleName() + "]lnuUltimaFila: " + lnuUltimaFila);
			HSSFRow filaCero = sheet.getRow(0);
			if (filaCero.getCell(8).toString().toUpperCase().contains("PRECIO")
					&& filaCero.getCell(11).toString().toUpperCase().contains("ID BROADVIEW")) {
				for (int xFila = 1; xFila < lnuUltimaFila; xFila++) {
					HSSFRow row = sheet.getRow(xFila);
					if (row != null) {
						ParamCargaCostosDTO precio = new ParamCargaCostosDTO();
						HSSFCell cellPrecio = row.getCell(8);
						if (cellPrecio != null) {
							cellPrecio.setCellType(Cell.CELL_TYPE_STRING);
							precio.setPrecio(cellPrecio.toString());
						}
						HSSFCell cellIdBroadView = row.getCell(11);
						if (cellIdBroadView != null) {
							cellIdBroadView.setCellType(Cell.CELL_TYPE_STRING);
							precio.setIdBroadView(cellIdBroadView.toString());
						}
						precios.add(precio);
					}
				}
			}
		}
		if (d.getName().endsWith(".xlsx")) {
			FileInputStream file;
			XSSFWorkbook workbook = null;
			try {
				file = new FileInputStream(d);
				workbook = new XSSFWorkbook(file);
			} catch (IOException e) {
				e.printStackTrace();
			}
			XSSFSheet sheet = workbook.getSheetAt(0);
			int lnuUltimaFila = sheet.getPhysicalNumberOfRows();
			logger.debug("leerCargaTitulosXLS[" + this.getClass().getSimpleName() + "]lnuUltimaFila: " + lnuUltimaFila);
			XSSFRow filaCero = sheet.getRow(0);
			if (filaCero.getCell(8).toString().toUpperCase().contains("PRECIO")
					&& filaCero.getCell(11).toString().toUpperCase().contains("ID BROADVIEW")) {
				for (int xFila = 1; xFila < lnuUltimaFila; xFila++) {
					XSSFRow row = sheet.getRow(xFila);
					if (row != null) {
						ParamCargaCostosDTO precio = new ParamCargaCostosDTO();
						XSSFCell cellPrecio = row.getCell(8);
						if (cellPrecio != null) {
							cellPrecio.setCellType(Cell.CELL_TYPE_STRING);
							precio.setPrecio(cellPrecio.toString());
						}
						XSSFCell cellIdBroadView = row.getCell(11);
						if (cellIdBroadView != null) {
							cellIdBroadView.setCellType(Cell.CELL_TYPE_STRING);
							precio.setIdBroadView(cellIdBroadView.toString());
						}
						precios.add(precio);
					}
				}
			}
		}
		RespuestaCargaExcel response = new RespuestaCargaExcel();
		ResponseCargaTitulosDTO result = new ResponseCargaTitulosDTO();
		result.setDatosCarga(new CargaPrincipalDTO());
		List<Titulo> lista = tituloDummyDAO.buscarTitulosPorLista(tit.getIdLista());
		Map<String, Titulo> titulos = new HashMap<>();
		for (Titulo item : lista) {
			titulos.put(item.getIdBv(), item);
		}
		result.getDatosCarga().setTOTAL_REGISTROS(String.valueOf(titulos.size()));
		for (ParamCargaCostosDTO precio : precios) {
			if (Objects.nonNull(titulos.get(precio.getIdBroadView()))) {
				if (!esNumero(precio.getPrecio())) {
					response.getErroresControlados().add(titulos.get(precio.getIdBroadView()));
					titulos.remove(precio.getIdBroadView());
				} else {
					response.getExitos().add(titulos.get(precio.getIdBroadView()));
					titulos.remove(precio.getIdBroadView());
				}
			} else {
				tit = new Titulo();
				tit.setIdBv(precio.getIdBroadView());
				tit.setPrecioAnual(precio.getPrecio());
				response.getErroresNoControlados().add(tit);
			}
		}
		if (titulos.size() != 0) {
			for (Entry<String, Titulo> total : titulos.entrySet()) {
				response.getErroresNoControlados().add(total.getValue());
			}
		}
		if (response.getErroresControlados().isEmpty() && response.getErroresNoControlados().isEmpty()) {
			for (ParamCargaCostosDTO precio : precios) {
				tituloDummyDAO.actualizarCostos(precio.getPrecio(), precio.getIdBroadView());
			}
			result.setMensaje("Todos los costos se cargaron con exito.");
		} else {
			result.setMensaje("Existen detalles en el documento.");
		}
		result.getDatosCarga().setTOTAL_CARGADOS(String.valueOf(response.getExitos().size()));
		result.getDatosCarga().setTOTAL_ERROR(
				String.valueOf(response.getErroresNoControlados().size() + response.getErroresControlados().size()));
		response.setResponse(result);
		return response;
	}

	private static boolean esNumero(String str) {
		if (str == null || str.isEmpty()) {
			return false;
		}
		try {
			Double.parseDouble(str);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}

	public RespuestaCargaExcel cargaMasivaDeTitulos2(Titulo titulo, ContratoDTO contrato, TipoCargaDocumento tc,
			File fileUpload, String fileName, Lista listaActual, String expediente) throws TituloException {
		RespuestaCargaExcel r = new RespuestaCargaExcel();
		if (!tc.equals(TipoCargaDocumento.COSTOS)) {
			logger.debug("	");
			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(uriCarga + contrato.getIdContrato() + expediente + fileName);

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

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

				logger.debug(" RFD 24 02 2020 URI LANZADA [{}]",
						dlaRestClientFactory.getClient(DLARestServices.CARGA_TITULOS));

				DLARestClient c = dlaRestClientFactory.getClient(DLARestServices.CARGA_TITULOS);

				// Arma request para rest
				ParamCargaTitulosDTO request = new ParamCargaTitulosDTO();
				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()));

				logger.info("{}", request.getFechaInicioContrato());

				String sFechaInicio = "";
				Date dFechaInicio = titulo.getFechaInicio();

				logger.info("{}", dFechaInicio);

				if (dFechaInicio != null) {
					cal.setTime(dFechaInicio);
					cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 1);
					sFechaInicio = fecha.format(cal.getTime());
				}

				logger.info("{}", sFechaInicio);

				logger.info("{}", request.getFechaInicioContrato());
				logger.info("{}", dFechaInicio);

				request.setFechaInicioListaNueva(sFechaInicio);

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

				if (!response.isResultado()) {
					logger.debug("Error en el response  [{}]", response.getMensaje());
					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, "titulo", Titulo.class);
					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");
			} catch (TransaccionException 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;
			}

			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;
		} else {
			r = actualizarCostos(contrato, fileUpload, fileName, titulo);
			Double costo = tituloDummyDAO.buscarTotalCostoTitulos(contrato.getIdContrato(), Lista.MIRROR);
			costo = costo != null ? costo : 0d;
			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);
			Lista lista = listaDAO.buscarListaActual(contrato.getIdContrato(), Lista.MIRROR);
			r.setLista(lista);
			return r;
		}
	}

	public void guardaLoteReal(List<Titulo> titulos) {
		int total = titulos.size();
		int step = 0;
		int end = (total / CHUNK) + 1;

		logger.info("Total [{}]", total);
		logger.info("End [{}]", end);
		if (total != 0) {
			do {
				int from = step * CHUNK;
				int to = from + CHUNK > total ? from + (total % CHUNK) : from + CHUNK;
				step++;
				logger.info(" step[{}] from [{}]   ,  to [{}]", new Object[] { step, from, to - 1 });

				List<Titulo> p = titulos.subList(from, to);
				tituloChunkDAO.aReal(p);

			} while (step != end);
		}
	}

	public List<CatalogoDTO> buscaTipoCargaDocumento(ContratoDTO ct) {
		List<TipoCargaDocumento> r = new ArrayList<TipoCargaDocumento>();

		// Aqui se define que tipo de moimiento de carga se da
		if (ct.getIdTipoMovimiento().intValue() == 3) {
			Lista lista = listaDAO.buscarListaActual(ct.getIdContrato(), Lista.MIRROR);
			List<Titulo> titulos = tituloDAO.buscarTitulosPorLista(lista.getIdLista());

			logger.info("{} {}", titulos, titulos.size());

			boolean tIdSap = false;
			for (Titulo titulo : titulos)
				if (titulo.getIdSap() != null) {

					logger.info("{}", titulo);

					tIdSap = true;
					break;
				}

			logger.info("{}", tIdSap);
			logger.info("{} {}", lista.getIdContratoCreacion(), ct.getIdContrato());

			if (lista.getIdContratoCreacion().equals(ct.getIdContrato())) {
				r.add(TipoCargaDocumento.ANEXAR);

				if (!tIdSap)
					r.add(TipoCargaDocumento.REEMPLAZAR);

				logger.info("{}", r);

			} else
				r.add(TipoCargaDocumento.ANEXAR);
		} else {
			r.add(TipoCargaDocumento.ANEXAR);
			r.add(TipoCargaDocumento.REEMPLAZAR);
		}
		r.add(TipoCargaDocumento.COSTOS);
		List<Lista> l = listaDAO.buscarListasPorContratoCreacion(ct.getIdContrato(), Lista.MIRROR);

		logger.info("{} {}", l, l.size());

		if (l.size() == 0)
			r.add(TipoCargaDocumento.NUEVA);

		List<CatalogoDTO> tiposCarga = new ArrayList<CatalogoDTO>();

		for (TipoCargaDocumento tipo : r)
			tiposCarga.add(new CatalogoDTO(tipo.getValue(), tipo.getDesc()));

		return tiposCarga;
	}

	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 void actualizaEstatusTitulo(String estatus, Long idContrato) {
		tituloDAO.actualizarEstatusSapTitulo(estatus, idContrato);
	}
}
