package com.mx.dla.dda.carga.suscriptor.bos;

import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.admin.presupuestos.dtos.PlantillaDTO;
import com.mx.dla.dda.carga.suscriptor.daos.CargaSuscriptorDAO;
import com.mx.dla.dda.carga.suscriptor.dtos.CargaSuscripcionBD;
import com.mx.dla.dda.carga.suscriptor.dtos.CargaSuscripcionDTO;
import com.mx.dla.dda.carga.suscriptor.dtos.ParamCalculoCostoSuscriptoresDTO;
import com.mx.dla.dda.carga.suscriptor.dtos.ResponseCalculoCostoSuscriptoresDTO;
import com.mx.dla.dda.catalogos.daos.CatalogosDAO;
import com.mx.dla.dda.catalogos.dtos.EmpresaVentaDTO;
import com.mx.dla.dda.catalogos.dtos.PaisVentaDTO;
import com.mx.dla.dda.catalogos.dtos.RegionVentaDTO;
import com.mx.dla.dda.contrato.transaccion.exceptions.dtos.TransaccionException;
import com.mx.dla.dda.excelMapper.bos.ExcelMapperTransform;
import com.mx.dla.dda.excelMapper.context.ExcelMapperContext;
import com.mx.dla.dda.excelMapper.dtos.ExcelMapper;
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;
import com.mx.dla.global.bos.ReadExcelBO;
import com.mx.dla.global.dtos.FilaExcelDTO;

@Component
public class CargaSuscriptorBO extends BaseBO {

	private final String[] meses = { "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov",
			"Dic" };

	private final DecimalFormat formateador = new DecimalFormat("###,###");

	@Autowired
	private CargaSuscriptorDAO cargaSuscriptorDAO;

	@Autowired
	private CatalogosDAO catalogosDAO;

	@Autowired
	public DLARestClientFactory dlaRestClientFactory;

	@Autowired
	private ReadExcelBO readExcelBO;

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

	private Map<Integer, Integer> mesesMap = new HashMap<Integer, Integer>();

	@Autowired
	protected ExcelMapperTransform excelMapperTransform;

	@Autowired
	private ExcelMapperContext excelMapperContext;

	/*
	 * @See Metodo que regresa dada un mes y año los datos de susctipones actual y
	 * de 5 meses atras.
	 * 
	 */
	public List<CargaSuscripcionDTO> getDatosSuscripcion(Integer mes, Integer anio) throws ResultMapException, SQLException, TransaccionException {

		List<String> fechas = new ArrayList<String>();
		CargaSuscripcionDTO suscripcion = new CargaSuscripcionDTO();
		List<CargaSuscripcionDTO> listado = new ArrayList<CargaSuscripcionDTO>();

		try {
			Date inicio = crearFecha("01/" + mes + "/" + anio);
			for (int i = 0; i <= 5; i++) {
				mesesMap.put(i, getMesFecha(inicio, i));
				fechas.add(restarMesFecha(inicio, i));
				switch (i) {
				case 1:
					suscripcion.setMes1(getFechaFormato(inicio, i));
					break;
				case 2:
					suscripcion.setMes2(getFechaFormato(inicio, i));
					break;
				case 3:
					suscripcion.setMes3(getFechaFormato(inicio, i));
					break;
				case 4:
					suscripcion.setMes4(getFechaFormato(inicio, i));
					break;
				case 5:
					suscripcion.setMes5(getFechaFormato(inicio, i));
					break;
				case 0:
					suscripcion.setMesActual(getFechaFormato(inicio, i));
					break;
				}
			}

			listado.add(suscripcion);

			List<EmpresaVentaDTO> empresas = catalogosDAO.getEmpresasVenta();
			List<CargaSuscripcionBD> resultado = cargaSuscriptorDAO.obtenerCargaSuscripciones(fechas);
			boolean existeReg = false;
			boolean regActual = false;
			suscripcion = new CargaSuscripcionDTO();

			for (EmpresaVentaDTO empresa : empresas) {
				for (CargaSuscripcionBD carga : resultado) {
					if (empresa.getIdEmpresa().intValue() == carga.getIdEmpresa()) {
						this.asignarValores(carga, suscripcion, mes);
						existeReg = true;

						if (carga.getMes().intValue() == mes)
							regActual = true;
					}
				}

				if (existeReg) {
					if (!regActual) {
						suscripcion.setMesActual("");
						suscripcion.setIdCarga(0l);
					}
				} else {
					suscripcion.setIdEmpresa(empresa.getIdEmpresa());
					suscripcion.setMesActual("");
					suscripcion.setEmpresa(empresa.getDescripcion());

					PaisVentaDTO pais = catalogosDAO.getPaisXId(empresa.getIdPais());
					RegionVentaDTO region = catalogosDAO.getRegionXId(pais.getIdRegion());
					suscripcion.setRegion(region.getDescripcion());
					suscripcion.setIdEmpresa((empresa.getIdEmpresa()));
				}
				this.formatearListado(suscripcion);
				logger.debug("suscripcion " + suscripcion.toString());
				listado.add(suscripcion);
				existeReg = false;
				regActual = false;
				suscripcion = new CargaSuscripcionDTO();
			}
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		} catch (ParseException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		}
		CargaSuscripcionDTO suspcionAux = listado.get(0);
		listado.remove(0);
		Collections.sort(listado);
		List<CargaSuscripcionDTO> listadoAux = new ArrayList<CargaSuscripcionDTO>();
		listadoAux.add(suspcionAux);
		listadoAux.addAll(listado);
		return listadoAux;
	}

	private void asignarValores(CargaSuscripcionBD itemBD, CargaSuscripcionDTO itemDTO, Integer mes) {
		itemDTO.setEmpresa(itemBD.getEmpresa());
		itemDTO.setRegion(itemBD.getRegion());
		itemDTO.setIdEmpresa((itemBD.getIdEmpresa()));
		logger.debug("B:" + itemBD.toString());
		if (mes.intValue() == itemBD.getMes().intValue()) {
			itemDTO.setIdCarga(itemBD.getIdCarga());
			itemDTO.setMesActual(formateador.format(itemBD.getSuscriptores()));
		} else {
			for (int i = 1; i <= 5; i++) {
				if (mesesMap.get(i).intValue() == itemBD.getMes().intValue()) {
					if (i == 1)
						itemDTO.setMes1(formateador.format(itemBD.getSuscriptores()));
					if (i == 2)
						itemDTO.setMes2(formateador.format(itemBD.getSuscriptores()));
					if (i == 3)
						itemDTO.setMes3(formateador.format(itemBD.getSuscriptores()));
					if (i == 4)
						itemDTO.setMes4(formateador.format(itemBD.getSuscriptores()));
					if (i == 5)
						itemDTO.setMes5(formateador.format(itemBD.getSuscriptores()));
				}
			}
		}
		logger.debug(itemDTO.toString());
	}

	public void actualizarSuscriptores(List<CargaSuscripcionDTO> suscriptores) throws ResultMapException, SQLException, TransaccionException, SocketTimeoutException, IOException {
		String mes = "";
		try {
			for (CargaSuscripcionDTO item : suscriptores) {
				if (item.getIdCarga() == 0 && item.getMesActual() != null) {
					mes = item.getFecha();
					this.nuevoCargaSuscripcion(item);
				} else if (item.getIdCarga() != 0 && item.getMesActual() != null) {
					mes = item.getFecha();
					this.updateCargaSuscripcion(item);
				} else if (item.getIdCarga() != 0 && item.getMesActual() == null) {
					logger.info("idcarga:" + item.getIdCarga());
					cargaSuscriptorDAO.eliminarCarga(item.getIdCarga());
				}
			}
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		}
		// Se acaba de actualizar o hacer la carga; se notifica al rest
		try {
			logger.info("Se inicia comunicacion para el calculo de costo.");
			ParamCalculoCostoSuscriptoresDTO request = new ParamCalculoCostoSuscriptoresDTO();
			request.setFechaMes(mes);
			DLARestClient client = dlaRestClientFactory.getClient(DLARestServices.COSTO_SUSCRIPTORES);
			ResponseCalculoCostoSuscriptoresDTO response = client.get(request,
					ResponseCalculoCostoSuscriptoresDTO.class);
			logger.info("Se ha notificado Carga Suscriptores. Mensaje [" + response.getMensaje() + "]");
			logger.info("Se ha notificado Carga Suscriptores. Resultado [" + response.getResultado() + "]");
		} catch (PersistenceException e) {
			logger.error("Error al notificar Carga Suscriptores.", e);
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		} catch (DataAccessException e) {
			logger.error("Error al notificar Carga Suscriptores.", e);
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		}
	}

	private void nuevoCargaSuscripcion(CargaSuscripcionDTO suscriptor) throws ResultMapException, SQLException, TransaccionException {
		CargaSuscripcionBD itemBD = new CargaSuscripcionBD();
		itemBD.setIdEmpresa(suscriptor.getIdEmpresa());
		itemBD.setFecha((suscriptor.getFecha()));
		itemBD.setSuscriptores(Integer.parseInt(suscriptor.getMesActual().replace(",", "")));
		try {
			cargaSuscriptorDAO.nuevoCargaSuscripcion(itemBD);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		}
	}

	private void updateCargaSuscripcion(CargaSuscripcionDTO suscriptor) throws ResultMapException, SQLException, TransaccionException {
		CargaSuscripcionBD itemBD = new CargaSuscripcionBD();
		itemBD.setIdCarga(suscriptor.getIdCarga());
		itemBD.setSuscriptores(Integer.parseInt(suscriptor.getMesActual().replace(",", "")));
		cargaSuscriptorDAO.updateCargaSuscripcion(itemBD);
		try {
			cargaSuscriptorDAO.updateCargaSuscripcion(itemBD);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		}
	}

	private void formatearListado(CargaSuscripcionDTO item) {
		if (item.getMes1() == null)
			item.setMes1("");
		if (item.getMes2() == null)
			item.setMes2("");
		if (item.getMes3() == null)
			item.setMes3("");
		if (item.getMes4() == null)
			item.setMes4("");
		if (item.getMes5() == null)
			item.setMes5("");
	}

	public String getFecha(Date fecha) {
		DateFormat fechaHora = new SimpleDateFormat("dd/MM/yyyy");
		return fechaHora.format(fecha);
	}

	public Date crearFecha(String valor) throws ParseException {
		DateFormat formatter1 = new SimpleDateFormat("dd/MM/yy");
		Date fecha = formatter1.parse(valor);
		return fecha;
	}

	public String restarMesFecha(Date fecha, int mes) throws ParseException {
		Calendar calFecha = Calendar.getInstance();
		calFecha.setTime(fecha);
		calFecha.add(Calendar.MONTH, (mes * -1));
		return "01/" + (calFecha.get(Calendar.MONTH) + 1) + "/" + calFecha.get(Calendar.YEAR);
	}

	public Integer getMesFecha(Date fecha, int mes) throws ParseException {
		Calendar calFecha = Calendar.getInstance();
		calFecha.setTime(fecha);
		calFecha.add(Calendar.MONTH, (mes * -1));
		return calFecha.get(Calendar.MONTH) + 1;
	}

	public String getFechaFormato(Date fecha, int mes) {
		Calendar calFecha = Calendar.getInstance();
		calFecha.setTime(fecha);
		calFecha.add(Calendar.MONTH, (mes * -1));
		return meses[calFecha.get(Calendar.MONTH)] + "-" + calFecha.get(Calendar.YEAR);
	}

	public List<CargaSuscripcionDTO> ordenar(List<CargaSuscripcionDTO> lista) {
		for (int j = 1; j < lista.size() - 1; j++) {
			for (int i = 1; i < lista.size() - 1 - j; i++) {
				if (lista.get(i).getRegion().compareTo(lista.get(i + 1).getRegion()) > 0) {
					String aux;
					aux = lista.get(i).getRegion();
					lista.set(i, lista.get(i + 1));
					lista.get(i + 1).setRegion(aux);
				}

			}
		}
		return lista;
	}

	public Map<String, String> obtenInformacionExcel(File fileUpload, String fileName) throws TransaccionException {
		Map<String, String> informacionExcel = new HashMap<String, String>();
		List<FilaExcelDTO> listaInformacionExcel = null;
		try {
			listaInformacionExcel = readExcelBO.obtenerDatosExcel(uriCarga, fileUpload, fileName);
			for (FilaExcelDTO fila : listaInformacionExcel) {
				logger.debug("[{}] -- [{}]", fila.getFila().get(0), fila.getFila().get(1));
				informacionExcel.put(fila.getFila().get(0), fila.getFila().get(1));
			}
		} catch (IOException e) {
			logger.error("[{}]", e);
			logger.debug("[{}]", e);
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);
		} catch (ExcelMapperException e) {
			logger.error("[{}]", e);
			logger.debug("[{}]", e);
			throw new TransaccionException("Error al cargar los datos de actualizacion " + e);

		}
		return informacionExcel;
	}

	public byte[] obtenPlantilla(String[] empresas) {
		byte[] plantilla = null;
		List<PlantillaDTO> lista = new ArrayList<PlantillaDTO>();
		ExcelMapper mapper = null;
		for (String emp : empresas) {
			PlantillaDTO li = new PlantillaDTO();
			li.setEmpresa(emp);
			lista.add(li);
		}
		try {

			mapper = excelMapperContext.getExcelMapper("plantillaCargaSuscriptores");
			plantilla = excelMapperTransform.transformObjectToExcel(lista, mapper, PlantillaDTO.class);
		} catch (IllegalArgumentException e) {
			logger.error("[{}]", e);
		} catch (ExcelMapperException e) {
			logger.error("[{}]", e);
		} catch (IOException e) {
			logger.error("[{}]", e);
		}

		return plantilla;
	}
}