package com.mx.dla.dda.admin.catalogos.tipoCambioAddOns.bos;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
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.commons.io.FileUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.result.ResultMapException;
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.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.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.mx.dla.dda.admin.catalogos.addons.daos.CatalogoAddOnsDAO;
import com.mx.dla.dda.admin.catalogos.addons.dtos.EstudioAddonDTO;
import com.mx.dla.dda.admin.catalogos.estudios.daos.CatalogoEstudiosDAO;
import com.mx.dla.dda.admin.catalogos.tipoCambio.dtos.TipoCambioDTO;
import com.mx.dla.dda.admin.catalogos.tipoCambioAddOns.daos.TipoCambioAddOnsDAO;
import com.mx.dla.dda.admin.catalogos.tipoCambioAddOns.dtos.MesTiposAddOnsCTDTO;
import com.mx.dla.dda.admin.catalogos.tipoCambioAddOns.dtos.PaisAddOnsVDTO;
import com.mx.dla.dda.admin.catalogos.tipoCambioAddOns.dtos.TipoCambioAddOnsDTO;
import com.mx.dla.dda.admin.catalogos.tipoCambioAddOns.dtos.TipoCambioPaisMesAddOnsDTO;
import com.mx.dla.dda.contrato.transaccion.exceptions.dtos.TransaccionException;
import com.mx.dla.dda.excelMapper.bos.ExcelMapperTransform;
import com.mx.dla.dda.excelMapper.dtos.ExcelMapper;
import com.mx.dla.dda.excelMapper.dtos.ExcelRule;
import com.mx.dla.dda.excelMapper.exceptions.ExcelMapperException;
import com.mx.dla.dda.general.constants.MesesAnio;
import com.mx.dla.dda.reporte.cpview.dtos.ReporteCostPerViewVistaDTO;
import com.mx.dla.dda.reporte.cpview.exception.ReportCostPerViewException;
import com.mx.dla.global.bos.BaseBO;

@Service
public class TipoCambioAddOnsBO extends BaseBO {

	@Autowired
	private CatalogoEstudiosDAO catalogoEstudiosDAO;

	@Autowired
	private CatalogoAddOnsDAO catalogoAddOnsDAO;

	@Autowired
	private TipoCambioAddOnsDAO tipoCambioAddOnsDAO;

	@Autowired
	protected ExcelMapperTransform excelMapperTransform;

	public static String getExcelColumnName(int number) {
		final StringBuilder sb = new StringBuilder();

		int num = number - 1;
		while (num >= 0) {
			int numChar = (num % 26) + 65;
			sb.append((char) numChar);
			num = (num / 26) - 1;
		}
		return sb.reverse().toString();
	}

	public InputStream obtenerStreamReporte(Long idEstudio, int anio) throws IllegalArgumentException,
			IllegalAccessException, ParseException, ExcelMapperException, IOException, ReportCostPerViewException {
		ReporteCostPerViewVistaDTO reporte = null;
		ExcelMapper mapper = new ExcelMapper();
		List<ExcelRule> rules = new ArrayList<ExcelRule>();
		InputStream stream = null;
		byte[] info = null;
		String[][] tabla = null;
		reporte = obtenerReporteMensualVista(idEstudio, anio);

		if (reporte.getTabla() != null)
			tabla = reporte.getTabla();
		else
			tabla = new String[0][0];

		String[] rheader = reporte.getRheader();

		mapper.setClassName(ReporteCostPerViewVistaDTO.class.getName());
		mapper.setFirstRow(1);
		mapper.setId("PlantillaTipoCambioAddOns");
		mapper.setParent(null);
		mapper.setSkipErrors(true);
		mapper.setStartRowIndex(0);
		
		if(tabla.length > 0) {
			for (int i = 0; i < tabla[0].length; i++) {
				ExcelRule rule = new ExcelRule();
				List<String> headers = new ArrayList<String>();
				rule.setColumn(getExcelColumnName(i + 1));
	
				headers.add(rheader[i]);
	
				rule.setHeaders(headers);
	
				rules.add(rule);
			}
		} else {
			for (int i = 0; i < 13; i++) {
				ExcelRule rule = new ExcelRule();
				List<String> headers = new ArrayList<String>();
				rule.setColumn(getExcelColumnName(i + 1));
	
				headers.add(rheader[i]);
	
				rule.setHeaders(headers);
	
				rules.add(rule);
			}
		}

		mapper.setRules(rules);

		info = excelMapperTransform.transformObjectToExcel(tabla, mapper, String.class);
		stream = new ByteArrayInputStream(info);

		return stream;
	}

	public ReporteCostPerViewVistaDTO obtenerReporteMensualVista(Long idEstudio, int anio)
			throws ParseException, IllegalArgumentException, IllegalAccessException {
		ReporteCostPerViewVistaDTO reporte = new ReporteCostPerViewVistaDTO();

		reporte.setTabla(obtenReporteMensualTabla(idEstudio, anio)); // Paises
		reporte.setRheader(obtenerHeaderRubro()); // Meses

		return reporte;
	}

	public String[][] obtenReporteMensualTabla(Long idEstudio, int anio)
			throws IllegalArgumentException, IllegalAccessException {

		String[][] tabla = null;
		List<TipoCambioPaisMesAddOnsDTO> datos2 = null;

		if (idEstudio != null) {
			datos2 = tipoCambioAddOnsDAO.obtnerPaisMes(idEstudio, anio);
		}

		if (datos2 != null && !datos2.isEmpty()) {
			for (TipoCambioPaisMesAddOnsDTO item : datos2) {
				String[] fila = item.getFieldsAsRow();
				if (tabla == null) {
					tabla = new String[1][fila.length];
				} else {
					tabla = Arrays.copyOf(tabla, tabla.length + 1);
				}

				tabla[tabla.length - 1] = fila;
			}
		}
		return tabla;
	}

	public String[] obtenerHeaderRubro() {
		List<String> rubros = new ArrayList<String>();

		rubros.add("País");
		rubros.add("Enero");
		rubros.add("Febrero");
		rubros.add("Marzo");
		rubros.add("Abril");
		rubros.add("Mayo");
		rubros.add("Junio");
		rubros.add("Julio");
		rubros.add("Agosto");
		rubros.add("Septiembre");
		rubros.add("Octubre");
		rubros.add("Noviembre");
		rubros.add("Diciembre");

		String[] rubrosArr = new String[rubros.size()];
		rubrosArr = rubros.toArray(rubrosArr);
		return rubrosArr;
	}

	public List<Integer> obtenerAnnios() {

		List<Integer> annios = new ArrayList<Integer>();
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		int annio = calendar.get(Calendar.YEAR) - 2;

		for (int i = 0; i < 4; i++) {
			annios.add(annio++);
		}

		return annios;
	}

	public List<EstudioAddonDTO> obtenerEstudios() throws TransaccionException {
		List<EstudioAddonDTO> estudios = null;

		try {
			estudios = catalogoEstudiosDAO.catalogoEstudiosAddOn();
		} catch (Exception ex) {
			throw new TransaccionException("Error al obtener estudios.", ex);
		}

		return estudios;
	}

	public List<TipoCambioDTO> obtenerNombreMeses() throws TransaccionException {
		List<TipoCambioDTO> nombreMeses = new ArrayList<TipoCambioDTO>();
		nombreMeses.add(new TipoCambioDTO());
		TipoCambioDTO aux = null;
		MesesAnio ma = null;

		for (int i = 1; i <= 12; i++) {
			aux = new TipoCambioDTO();
			ma = MesesAnio.parse("" + i);
			aux.setIdTipoCambio(Long.valueOf(String.valueOf(i)));
			aux.setDescripcion(ma.getDesc());
			nombreMeses.add(aux);
		}

		return nombreMeses;
	}

	public List<MesTiposAddOnsCTDTO> obtenerMesTipo(Long idEstudio, Integer annio, List<TipoCambioDTO> nombreMeses)
			throws ResultMapException, SQLException, TransaccionException {
		List<MesTiposAddOnsCTDTO> mesesTipo = new ArrayList<MesTiposAddOnsCTDTO>();
		MesTiposAddOnsCTDTO aux = null;
		List<PaisAddOnsVDTO> paises = Collections.emptyList();
		EstudioAddonDTO estudio = new EstudioAddonDTO();
		try {
			paises = tipoCambioAddOnsDAO.obtenerPaisesAddOnXEstudio(idEstudio);
			estudio = catalogoEstudiosDAO.catalogoEstudiosAddOnById(idEstudio);
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al obtener paises." + e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al obtener paises." + e);
		}

		for (PaisAddOnsVDTO pais : paises) {

			aux = new MesTiposAddOnsCTDTO();
			aux.setNombre(pais.getNombre());
			aux.setTipos(completaMesTipos(estudio.getIdEstudio(), pais.getIdPaisAddOn(), pais.getIdRelacion(), annio,
					nombreMeses));
			mesesTipo.add(aux);
		}

		return mesesTipo;
	}

	public List<TipoCambioAddOnsDTO> completaMesTipos(Integer idEstudio, Long idPais, Long idRelacion, Integer annio,
			List<TipoCambioDTO> nombreMeses) throws TransaccionException {
		Map<Long, TipoCambioAddOnsDTO> mesTiposM = new HashMap<Long, TipoCambioAddOnsDTO>();
		List<TipoCambioAddOnsDTO> mesPaises = null;
		TipoCambioAddOnsDTO tcpDto = null;
		Calendar calendar = Calendar.getInstance();
		String CADENA_VACIA = "";

		try {
			mesPaises = tipoCambioAddOnsDAO.obtnerMesTiposXPais(annio, idEstudio, idPais);

			if (mesPaises != null) {
				for (TipoCambioAddOnsDTO mt : mesPaises) {
					Calendar calAux1 = Calendar.getInstance();
					calAux1.setTime(mt.getMes());
					mesTiposM.put(Long.valueOf(calAux1.get(Calendar.MONTH) + 1), mt);
				}
			}

			mesPaises = new ArrayList<TipoCambioAddOnsDTO>();

			for (TipoCambioDTO mes : nombreMeses) {
				if (mesTiposM.get(mes.getIdTipoCambio()) != null) { // Si SI existe en BD (Ya existe este valor) ...
					tcpDto = mesTiposM.get(mes.getIdTipoCambio());
					Calendar calAux = Calendar.getInstance();
					calAux.setTime(tcpDto.getMes());
					if (calAux.get(Calendar.MONTH) + 1 == mes.getIdTipoCambio()) { // Si es del mes que estamos checando
																					// (en esta iteracion) ...
						if (tcpDto.getIdTipoCambio() != null) {
							tcpDto.setRecuperado(true);
							tcpDto.setValor(
									tcpDto.getValor() != null ? numberFormatter(tcpDto.getValor()) : CADENA_VACIA);
							mesPaises.add(mesTiposM.get(mes.getIdTipoCambio()));
						}
					}
				} else if (mesTiposM.get(mes.getIdTipoCambio()) == null) { // Si NO existe en BD (No existe este valor)
																			// ...
					if (mes.getIdTipoCambio() == null)
						continue;

					calendar.set(Calendar.YEAR, annio);
					calendar.set(Calendar.MONTH, (Integer.valueOf(String.valueOf(mes.getIdTipoCambio()))) - 1);
					calendar.set(Calendar.DAY_OF_MONTH, 1);

					tcpDto = new TipoCambioAddOnsDTO();
					tcpDto.setIdRelacion(idRelacion);
					tcpDto.setIdEstudio(Long.valueOf(String.valueOf(idEstudio)));
					tcpDto.setIdPais(idPais);
					tcpDto.setIdTipoCambio(null);
					tcpDto.setMes(calendar.getTime());
					tcpDto.setRecuperado(false);
					mesPaises.add(tcpDto);
				}
			}
		} catch (Exception ex) {
			throw new TransaccionException("Error al obtener los meses por tipo.", ex);
		}

		return mesPaises;
	}

	private String numberFormatter(String numberS) {
		NumberFormat formatter = new DecimalFormat("#0.00000");
		String numberD = "";
		try {
			numberD = formatter.format(Double.parseDouble(numberS));
		} catch (NumberFormatException nfe) {
			numberD = numberS;
		}
		return numberD;
	}

	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public boolean guardarCambios(List<MesTiposAddOnsCTDTO> mesesTipos)
			throws ResultMapException, SQLException, TransaccionException {
		boolean respuesta = false;

		try {
			if (mesesTipos != null) {
				for (MesTiposAddOnsCTDTO mt : mesesTipos) {
					logger.info("mes : [{}]", mt.getNombre());
					if (mt.getTipos() != null) {
						for (TipoCambioAddOnsDTO tcp : mt.getTipos()) {
							if (tcp.getRecuperado()) {
								tipoCambioAddOnsDAO.actualizaTipoCambio(tcp);
							} else {
								if (tcp.getValor() != null)
									tipoCambioAddOnsDAO.creaTipoCambio(tcp);
							}
						}
					}
				}

				respuesta = true;
			}
		} catch (PersistenceException e) {
			throw new TransaccionException("Error al obtener paises." + e);
		} catch (DataAccessException e) {
			throw new TransaccionException("Error al obtener paises." + e);
		}

		return respuesta;
	}

	public boolean cargaMasivaTipoCambioAddOn(Long anio, Long idEstudio, File fileUpload, String fileName,
			List<TipoCambioDTO> meses) throws ResultMapException, SQLException, TransaccionException {
		List<MesTiposAddOnsCTDTO> pasisesValores = new ArrayList<>();

		pasisesValores = cargarValoresTipoCambioAddOn(anio, idEstudio, fileUpload, fileName, meses);
		guardarCambios(pasisesValores);

		return true;
	}

	public List<MesTiposAddOnsCTDTO> cargarValoresTipoCambioAddOn(Long anio, Long idEstudio, File fileUpload,
			String fileName, List<TipoCambioDTO> meses) throws ResultMapException, SQLException, TransaccionException {
		List<MesTiposAddOnsCTDTO> valores = new ArrayList<>();
		List<TipoCambioAddOnsDTO> valoresPais;
		byte[] f;
		File d = null;
		TipoCambioAddOnsDTO valor;
		PaisAddOnsVDTO pais;
		String sigla = "";
		Long idRelacion;
		Calendar calendar = Calendar.getInstance();
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
		boolean existeValor = false;

		// Convierte el Stream a un Archivo.
		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) {
			logger.info(e1.getMessage(), e1);
		}

		// Proceso para formato "XLS"
		if (d.getName().endsWith(".xls")) {
			FileInputStream file;
			HSSFWorkbook workbook = null;

			try {
				file = new FileInputStream(d);
				workbook = new HSSFWorkbook(file);
			} catch (IOException e) {
				logger.info(e.getMessage(), e);
			}

			HSSFSheet sheet = workbook.getSheetAt(0);
			int lnuUltimaFila = sheet.getPhysicalNumberOfRows();
			HSSFRow filaCero = sheet.getRow(0);

			if (filaCero.getCell(0).toString().toUpperCase().contains("País")) {
				for (int xFila = 1; xFila < lnuUltimaFila; xFila++) {
					HSSFRow row = sheet.getRow(xFila);
					MesTiposAddOnsCTDTO tipoCambio = new MesTiposAddOnsCTDTO();
					valoresPais = new ArrayList<>();
					sigla = row.getCell(0).toString();

					try {
						pais = tipoCambioAddOnsDAO.obtenerPaisXSigla(sigla);
						idRelacion = catalogoAddOnsDAO.obtenerIdRelacion(idEstudio, pais.getIdPaisAddOn());
					} catch (PersistenceException e) {
						throw new TransaccionException("Error al obtener paises." + e);
					} catch (DataAccessException e) {
						throw new TransaccionException("Error al obtener paises." + e);
					}

					tipoCambio.setNombre(pais.getNombre());

					if (row != null && idRelacion != null) {
						for (int i = 1; i <= 12; i++) {
							if (row.getCell(i).toString() != null && !(row.getCell(i).toString()).trim().equals("")) {
								calendar.set(Calendar.YEAR, Integer.valueOf(String.valueOf(anio)));
								calendar.set(Calendar.MONTH, (Integer.valueOf(String.valueOf(i))) - 1);
								calendar.set(Calendar.DAY_OF_MONTH, 1);

								try {
									existeValor = tipoCambioAddOnsDAO.verificarExistenciaTipoCambioAddOn(idRelacion,
											sdf.format(calendar.getTime())) == 0 ? false : true;
								} catch (PersistenceException e) {
									throw new TransaccionException("Error al obtener paises." + e);
								} catch (DataAccessException e) {
									throw new TransaccionException("Error al obtener paises." + e);
								}

								valor = new TipoCambioAddOnsDTO();
								valor.setIdEstudio(idEstudio);
								valor.setIdPais(pais.getIdPaisAddOn());
								valor.setIdRelacion(idRelacion);
								valor.setMes(calendar.getTime());
								valor.setValor(row.getCell(i).toString());
								valor.setRecuperado(existeValor);
								valoresPais.add(valor);
							}
						}

						tipoCambio.setTipos(valoresPais);
					}

					valores.add(tipoCambio);
				}
			}
		}

		// Proceso para formato "XLSX"
		if (d.getName().endsWith(".xlsx")) {
			FileInputStream file;
			XSSFWorkbook workbook = null;
			String CADENA_VACIA = "";

			try {
				file = new FileInputStream(d);
				workbook = new XSSFWorkbook(file);
			} catch (IOException e) {
				e.printStackTrace();
			}

			XSSFSheet sheet = workbook.getSheetAt(0);
			int lnuUltimaFila = sheet.getPhysicalNumberOfRows();

			if (true) {
				for (int xFila = 1; xFila < lnuUltimaFila; xFila++) {
					XSSFRow row = sheet.getRow(xFila);
					MesTiposAddOnsCTDTO tipoCambio = new MesTiposAddOnsCTDTO();
					valoresPais = new ArrayList<>();
					sigla = row.getCell(0).toString();

					try {
						pais = tipoCambioAddOnsDAO.obtenerPaisXSigla(sigla);
						idRelacion = catalogoAddOnsDAO.obtenerIdRelacion(idEstudio, pais.getIdPaisAddOn());
					} catch (PersistenceException e) {
						throw new TransaccionException("Error al obtener paises." + e);
					} catch (DataAccessException e) {
						throw new TransaccionException("Error al obtener paises." + e);
					}

					if (row != null && idRelacion != null) {
						for (int i = 1; i <= 12; i++) {
							Double datoNum = null;

							try {
								datoNum = Double.parseDouble(row.getCell(i).toString());
							} catch (NumberFormatException e) {
								datoNum = null;
							}

							if (datoNum != null) {
								calendar.set(Calendar.YEAR, Integer.valueOf(String.valueOf(anio)));
								calendar.set(Calendar.MONTH, (Integer.valueOf(String.valueOf(i))) - 1);
								calendar.set(Calendar.DAY_OF_MONTH, 1);

								try {
									existeValor = tipoCambioAddOnsDAO.verificarExistenciaTipoCambioAddOn(idRelacion,
											sdf.format(calendar.getTime())) == 0 ? false : true;
								} catch (PersistenceException e) {
									throw new TransaccionException("Error al obtener paises." + e);
								} catch (DataAccessException e) {
									throw new TransaccionException("Error al obtener paises." + e);
								}

								valor = new TipoCambioAddOnsDTO();
								valor.setIdEstudio(idEstudio);
								valor.setIdPais(pais.getIdPaisAddOn());
								valor.setIdRelacion(idRelacion);
								valor.setMes(calendar.getTime());
								valor.setValor((datoNum > 0) ? datoNum.toString() : CADENA_VACIA);
								valor.setRecuperado(existeValor);
								valoresPais.add(valor);
							}
						}

						tipoCambio.setTipos(valoresPais);
					}

					valores.add(tipoCambio);
				}
			}
		}

		return valores;
	}
}