package com.mx.dla.dda.contrato.transaccion.titulos.bos;

import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;
import java.text.ParseException;
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.dtos.CatalogoDTO;
import com.mx.dla.dda.contrato.generales.daos.GeneralesDAO;
import com.mx.dla.dda.contrato.generales.dtos.ContratoDTO;
import com.mx.dla.dda.contrato.generales.enums.Movimiento;
import com.mx.dla.dda.contrato.titulo.constants.TipoCargaDocumento;
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.exception.TituloException;
import com.mx.dla.dda.contrato.transaccion.exceptions.dtos.TransaccionException;
import com.mx.dla.dda.contrato.transaccion.titulos.daos.TituloLoteTransDAO;
import com.mx.dla.dda.contrato.transaccion.titulos.daos.TituloTransDAO;
import com.mx.dla.dda.contrato.transaccion.titulos.dtos.TituloTransaccionExcel;
import com.mx.dla.dda.contrato.transaccion.titulos.dtos.TituloValidaEstatusDTO;
import com.mx.dla.dda.contrato.transaccion.ws.titulos.dtos.ParamCargaTitulosTRDTO;
import com.mx.dla.dda.contrato.transaccion.ws.titulos.dtos.ParamInventarioTitulosDTO;
import com.mx.dla.dda.contrato.transaccion.ws.titulos.dtos.ResponseCargaTitulosTRDTO;
import com.mx.dla.dda.contrato.transaccion.ws.titulos.dtos.ResponseNotificarInventarioTitulosDTO;
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 TituloTranBO extends BaseBO {

	@Autowired
	private GeneralesDAO generalesDAO;

	@Autowired
	private TituloLoteTransDAO tituloLoteDAO;

	@Autowired
	private TituloTransDAO tituloDAO;

	@Autowired
	private BeanPopulator beanPopulator;

	@Autowired
	protected ExcelMapperTransform excelMapperTransform;

	@Autowired
	private DLARestClientFactory dlaRestClientFactory;

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

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

	public void init(Long idContrato, Date fechaInicio, boolean isAuto)
			throws ResultMapException, SQLException, TransaccionException {
		Lista lista = null;
		try {
			lista = tituloDAO.buscarListaActual(idContrato, Lista.REAL);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
		if (lista == null)
			lista = this.crearListaNueva(idContrato, fechaInicio, Lista.REAL);
		if (!isAuto) {
			this.createMirrorEntities(idContrato);
		}
	}

	public Lista crearListaNueva(Long idContrato, Date fechaInicio, String prefix)
			throws ResultMapException, SQLException, TransaccionException {
		Lista l = new Lista(idContrato, fechaInicio, prefix);

		try {
			tituloDAO.insertarLista(l, prefix, idContrato, fechaInicio);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
		return l;
	}

	public Lista buscarListaActual(Long idContrato, String prefix)
			throws ResultMapException, SQLException, TransaccionException {
		try {
			Lista lista = tituloDAO.buscarListaActual(idContrato, prefix);
			return lista;
		} 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(Long idContrato) throws ResultMapException, SQLException, TransaccionException {

		try {
			tituloLoteDAO.borrarTitulosPorContratoTran(Lista.MIRROR, idContrato);
			tituloLoteDAO.borrarListaPorIdContrato(Lista.MIRROR, idContrato);
			tituloLoteDAO.moverLista(Lista.REAL, Lista.MIRROR, idContrato);
			tituloLoteDAO.moverTitulos(Lista.REAL, Lista.MIRROR, idContrato);
		} 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 guardarCambiosPermanente(Long idContrato)
			throws ResultMapException, SQLException, TransaccionException {
		TituloValidaEstatusDTO estatus = null;
		// may-17
		try {
			tituloLoteDAO.borrarTitulosPorContratoTran(Lista.REAL, idContrato);
			tituloLoteDAO.borrarListaPorIdContrato(Lista.REAL, idContrato);
			tituloLoteDAO.moverLista(Lista.MIRROR, Lista.REAL, idContrato);
			tituloLoteDAO.moverTitulos(Lista.MIRROR, Lista.REAL, idContrato);
			tituloLoteDAO.calcularMG(idContrato);
			// se recupera estatus del contrato
			estatus = this.validaEstatusContrato(idContrato);
			Long idEstatus = estatus.getEstatus();
			if (idEstatus == 5) {
				this.notificarInventarioTitulos(idContrato);
			} else {
				logger.info("notificarInventarioTitulos. No se Notifico, el contrato no esta vigente: " + idContrato);
			}
		} catch (PersistenceException e) {
			throw new TransaccionException("Error.calcularMG - al notificar el contrato:__", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error.calcularMG - al notificar el contrato:__", e);
		}
	}

	// may-17
	public TituloValidaEstatusDTO validaEstatusContrato(Long idContrato)
			throws ResultMapException, SQLException, TransaccionException {
		TituloValidaEstatusDTO estatuscontrato = null;
		try {
			estatuscontrato = tituloLoteDAO.validaEstatusContrato(idContrato);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error.calcularMG - al notificar el contrato:__", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error.calcularMG - al notificar el contrato:__", e);
		}

		return estatuscontrato;
	}

	// may-17
	public ResponseNotificarInventarioTitulosDTO notificarInventarioTitulos(Long idContrato)
			throws ResultMapException, SQLException, TransaccionException {

		ResponseNotificarInventarioTitulosDTO response = null;
		ParamInventarioTitulosDTO request = new ParamInventarioTitulosDTO();
		DLARestClient client = null;

		try {
			Long idContratoTrans = idContrato;

			client = dlaRestClientFactory.getClient(DLARestServices.WS_TRANSACCIONTITULOS_NOTIFICAR_INVENTARIOS);
			request.setIdContrato(idContratoTrans.toString());
			logger.debug("REQUEST - WS_TRANSACCIONTITULOS_NOTIFICAR_INVENTARIOS : [{}]", request);
			response = client.get(request, ResponseNotificarInventarioTitulosDTO.class);

			logger.debug("RESPONSE . WS_TRANSACCIONTITULOS_NOTIFICAR_INVENTARIOS : [{}]", response);

		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		} catch (SocketTimeoutException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		} catch (IOException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}

		return response;
	}

	public byte[] generateExcelReport(Long idLista)
			throws ExcelMapperException, IOException, ResultMapException, SQLException, TransaccionException {
		logger.info("Descargar archivo de excel [{}]", idLista);
		List<TituloTransaccionExcel> lista = new ArrayList<TituloTransaccionExcel>();
		try {
			lista = tituloDAO.obtenerTitulosExcel(idLista);
			if (lista == null || lista.isEmpty()) {
				lista = new ArrayList<TituloTransaccionExcel>();
				lista.add(beanPopulator.initObject(TituloTransaccionExcel.class));
			}
			logger.info("Descargar archivo, se han  obtenido [{}]", lista.size());

		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}

		byte[] fileExcel = excelMapperTransform.transformObjectToExcel(lista, "tituloTransaccionExportarLista",
				TituloTransaccionExcel.class);
		logger.debug("Descargar archivo, se ha generado binario.");
		return fileExcel;
	}

	public String generaNombreDescarga() {
		Date a = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
		String fileFileName = "plantilla-" + sdf.format(a) + ".xlsx";
		return fileFileName;
	}

	// jun-17
	public RespuestaCargaExcel cargaMasivaTitulos(String inicioLista, Long idContrato, String tc, File fileUpload,
			String fileName, Long idLista, String expediente) throws TituloException, ParseException, IOException,
			TransaccionException, ResultMapException, SQLException {

		RespuestaCargaExcel r = new RespuestaCargaExcel();
		idLista = this.ajustarListaEnmiendas(tc, inicioLista, idContrato, idLista);
		File archivo = this.guardarArchivoSistema(idContrato, expediente, fileName, fileUpload);
		ParamCargaTitulosTRDTO request = this.generaObjRequestWs(idContrato, expediente, idLista, tc,
				archivo.getCanonicalPath(), inicioLista);
		ResponseCargaTitulosTRDTO response = this.llamadaRestCargaTransaccionesTitulos(request, idContrato);
		r = this.actualizaLista(idContrato);

		r.setResponseTR(response);

		return r;
	}

	/*
	 * Metodo que crea una nueva lista. si el contrato es una enmienda y el usuario
	 * carga titulos vía excel, y ha seleccionado la opción nueva lista.
	 */
	public Long ajustarListaEnmiendas(String tc, String inicioLista, Long idContrato, Long idLista)
			throws ParseException, ResultMapException, SQLException, TransaccionException {
		if (tc.compareTo(TipoCargaDocumento.NUEVA.getValue()) == 0) {
			SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy");
			Date fecha = formatter.parse(inicioLista);
			Lista listaA = tituloDAO.buscarListaActual(idContrato, Lista.MIRROR);
			tituloDAO.actualizarFechaFinLista(listaA.getIdLista(), Lista.MIRROR, this.calculaFechaMenosUnDia(fecha));
			Lista nueva = this.crearListaNueva(idContrato, fecha, Lista.MIRROR);
			idLista = nueva.getIdLista();
		}
		return idLista;
	}

	public File guardarArchivoSistema(Long idContrato, String expediente, String fileName, File fileUpload)
			throws IOException {
		byte[] f = FileUtils.readFileToByteArray(fileUpload);
		File d = new File(uriCarga + idContrato + 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);
		return d;
	}

	public ParamCargaTitulosDTO generaObjetoRequestRest(Long idContrato, String expediente, Long idLista,
			String tipoCambio, String archivo, String fechaInicioLista) {
		ParamCargaTitulosDTO request = new ParamCargaTitulosDTO();

		request.setNombreArchivo(FilenameUtils.getName(archivo));
		request.setIdContrato(idContrato.toString());
		request.setIdListaActual(idLista.toString());
		request.setTipoLista(TipoCargaDocumento.NUEVA.getValue().equals(tipoCambio) ? "NUEVA" : "ACTUAL");
		request.setTipoCarga(TipoCargaDocumento.ANEXAR.getValue().equals(tipoCambio) ? "AGREGAR" : "REEMPLAZAR");
		request.setExpediente(expediente);
		request.setFechaInicioListaNueva(fechaInicioLista != null ? fechaInicioLista : "");
		logger.debug("Request carga titulos [{}]", request);
		return request;
	}

	// JUN-17 carga de titulos en transacciones
	public ParamCargaTitulosTRDTO generaObjRequestWs(Long idContrato, String expediente, Long idLista,
			String tipoCambio, String archivo, String fechaInicioLista) {
		ParamCargaTitulosTRDTO request = new ParamCargaTitulosTRDTO();

		request.setNombreArchivo(FilenameUtils.getName(archivo));
		request.setIdContrato(idContrato.toString());
		request.setIdListaActual(idLista.toString());
		request.setTipoLista(TipoCargaDocumento.NUEVA.getValue().equals(tipoCambio) ? "NUEVA" : "ACTUAL");
		request.setTipoCarga(TipoCargaDocumento.ANEXAR.getValue().equals(tipoCambio) ? "AGREGAR" : "REEMPLAZAR");
		request.setExpediente(expediente);
		request.setFechaInicioListaNueva(fechaInicioLista != null ? fechaInicioLista : "");
		logger.debug("Request carga transacciones titulos [{}]", request);

		return request;
	}

	public ResponseCargaTitulosDTO llamarRestCargaTitulos(ParamCargaTitulosDTO request, Long idContrato)
			throws TituloException, SocketTimeoutException, IOException, TransaccionException {

		DLARestClient c = dlaRestClientFactory.getClient(DLARestServices.CARGA_TITULOS);
		ResponseCargaTitulosDTO response = c.get(request, ResponseCargaTitulosDTO.class);
		logger.info("Response carga titulos [{}]", response);
		this.generaArchivoErrores(response, idContrato);
		return response;
	}

	// jun-17
	public ResponseCargaTitulosTRDTO llamadaRestCargaTransaccionesTitulos(ParamCargaTitulosTRDTO request,
			Long idContrato) throws TituloException, SocketTimeoutException, IOException, TransaccionException {

		DLARestClient c = dlaRestClientFactory.getClient(DLARestServices.WS_TRANSACCION_TITULOS_CARGA);
		ResponseCargaTitulosTRDTO response = c.get(request, ResponseCargaTitulosTRDTO.class);
		logger.info("Response carga titulos [{}]", response);

		this.generaExcelErrores(response, idContrato);
		return response;
	}

	// jun-17
	public void generaExcelErrores(ResponseCargaTitulosTRDTO response, Long idContrato)
			throws TituloException, IOException, TransaccionException {
		try {
			if (!response.getDatosCarga().getTOTAL_ERROR().equals("0")) {
				List<TituloTransaccionExcel> a = new ArrayList<TituloTransaccionExcel>();

				a = tituloDAO.obtenerTitulosExcelError(Long.parseLong(response.getDatosCarga().getID_CARGA()));
				logger.debug("{}", a.toString());
				byte[] fileExcel = excelMapperTransform.transformObjectToExcel(a, "tituloTransaccionExcel",
						TituloTransaccionExcel.class);

				File destFile = new File(uriErrores + idContrato + ".xlsx");
				FileUtils.writeByteArrayToFile(destFile, fileExcel);
			}

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

		} 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 generaArchivoErrores(ResponseCargaTitulosDTO response, Long idContrato) throws TituloException {
		try {
			if (!response.getDatosCarga().getTOTAL_ERROR().equals("0")) {
				List<TituloTransaccionExcel> a = new ArrayList<TituloTransaccionExcel>();

				a = tituloDAO.obtenerTitulosExcelError(Long.parseLong(response.getDatosCarga().getID_CARGA()));
				logger.debug("{}", a.toString());
				byte[] fileExcel = excelMapperTransform.transformObjectToExcel(a, "tituloTransaccionExcel",
						TituloTransaccionExcel.class);

				File destFile = new File(uriErrores + idContrato + ".xlsx");
				FileUtils.writeByteArrayToFile(destFile, fileExcel);
			}

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

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

	public RespuestaCargaExcel actualizaLista(Long idContrato) throws ResultMapException, SQLException, TransaccionException {
		RespuestaCargaExcel r = new RespuestaCargaExcel();
		try {
			Lista listaA = tituloDAO.buscarListaActual(idContrato, Lista.REAL);
			r.setLista(listaA);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
		return r;
	}

	public List<TipoCargaDocumento> buscaTipoCargaDocumento(Long idContrato) throws ResultMapException, SQLException, TransaccionException {
		List<TipoCargaDocumento> r = new ArrayList<TipoCargaDocumento>();
		r.add(TipoCargaDocumento.ANEXAR);
		r.add(TipoCargaDocumento.REEMPLAZAR);
		ContratoDTO contrato = null;

		try {
			contrato = generalesDAO.obtenerContrato(idContrato);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos.", e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos", e);
		}
		if (contrato.getIdTipoMovimiento().intValue() == Movimiento.Enmienda.getValor().intValue())
			r.add(TipoCargaDocumento.NUEVA);
		return r;
	}

	public List<CatalogoDTO> buscaListas(Long idContrato) throws ResultMapException, SQLException, TransaccionException {

		try {
			return tituloDAO.buscarCatListaByContrato(idContrato, Lista.REAL);
		} 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 List<Long> buscaListasValidas(Long idContrato) throws ResultMapException, SQLException, TransaccionException {

		try {
			return tituloDAO.buscarListaValidas(idContrato, Lista.REAL);
		} 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 byte[] buscarArchivoDeErrores(Long idContrato) throws IOException {
		File file = new File(uriErrores + idContrato + ".xlsx");
		logger.info("busca archivo en [{}]", file.getAbsolutePath());
		byte[] errores = FileUtils.readFileToByteArray(file);
		return errores;
	}

	public Integer buscarNumTitulosContratados(Long idContrato) throws IOException, ResultMapException, SQLException, TransaccionException {

		try {
			return tituloDAO.obtenerNumTitulos(idContrato);
		} 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 Date calculaFechaMenosUnDia(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();
	}
}