package com.ejie.ad74a.service.interoperabilidad;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.xml.ws.soap.SOAPFaultException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;

import com.ejie.ad74a.exceptions.Ad74aException;
import com.ejie.ad74a.exceptions.Ad74aWebserviceException;
import com.ejie.ad74a.model.NotifPostales;
import com.ejie.ad74a.util.constantes.Constantes;
import com.ejie.ad74a.ws.client.remesas.AA66Notificaciones;
import com.ejie.ad74a.ws.client.remesas.IdentCodPostalWs;
import com.ejie.ad74a.ws.client.remesas.IdentTramitadorCondicionalWs;
import com.ejie.ad74a.ws.client.remesas.IdentTramitadorWs;
import com.ejie.ad74a.ws.client.remesas.SituacionEntregaWs;
import com.ejie.ad74a.ws.client.remesas.ValidarCodPostalOutWs;
import com.ejie.ad74a.ws.client.remesas.ZipEnvioWs;

/**
 * Servicio que engloba la integracion con la pasarela de notificaciones AA66.
 * 
 * @author ¿?, JBHG
 *
 */
@Service(value = "Remesas")
public class RemesasServiceImpl implements RemesasService {

	/**
	 * Final static logger.
	 */
	private static final Logger logger = LoggerFactory.getLogger(RemesasServiceImpl.class);

	private static final String RUTA_DATOS_TEMPORAL = "ruta.datos.temporal";

	private final DateFormat situacionDateFormatter = new SimpleDateFormat("dd/MM/yyyy");

	private static final int VALIDARCODIGOPOSTAL_RESULTADO_OK = 1;

	@Autowired
	private AA66Notificaciones aa66Notificaciones;

	@Autowired
	private Properties appConfiguration;

	@Override
	public boolean validarCodigoPostal(String codigoPostal) {
		IdentCodPostalWs codPostalAValidar = new IdentCodPostalWs();
		codPostalAValidar.setCodPostal(codigoPostal);

		try {
			ValidarCodPostalOutWs respuesta = aa66Notificaciones.validarCodigoPostal(codPostalAValidar);
			return respuesta.getEsValido() == VALIDARCODIGOPOSTAL_RESULTADO_OK;
		} catch (SOAPFaultException e) {
			/*
			 * Un error en el procesamiento del servicio es tratado como que el código no es
			 * válido. (El servicio falla cuando el codigo postal no cumple que sea numérico
			 * y con 5 dígitos)
			 */
			return false;
		}
	}

	@Override
	public NotifPostales obtenerSituacionEnvio(NotifPostales notifPostales) throws Ad74aWebserviceException {
		logger.debug("obtenerSituacionEnvio {}", notifPostales);
		IdentTramitadorCondicionalWs envioSituacion = new IdentTramitadorCondicionalWs();
		// Envio indicado en el registro de la notificacion
		envioSituacion.setId(notifPostales.getIdEnvioPostal88());
		// Puesto indicado en el registro de la notificacion
		envioSituacion.setUidPuestoTramitador(notifPostales.getPostalInboxId88());
		envioSituacion.setTodas(true);
		/*
		 * Obtener situacion de la notificacion
		 */
		SituacionEntregaWs situacionEntregaWs = aa66Notificaciones.situacionEntregaEnvio(envioSituacion);
		/*
		 * Si se ha producido algun error:
		 */
		if (situacionEntregaWs.getError() != null
				&& StringUtils.isNotBlank(situacionEntregaWs.getError().getCodigoError())) {
			String message = situacionEntregaWs.getError().getDescripcionEuskera();
			if (com.ejie.ad74a.constantes.Constantes.ES
					.equalsIgnoreCase(LocaleContextHolder.getLocale().getLanguage())) {
				message = situacionEntregaWs.getError().getDescripcionCastellano();
			}
			message += " (" + situacionEntregaWs.getError().getCodigoError() + ")";
			throw new Ad74aWebserviceException(message);
		}
		/*
		 * Si no se ha producido error, se cargan los datos de la situación:
		 */
		try {
			notifPostales.setCodSituacionPostal88(situacionEntregaWs.getCodigoSituacion());
			notifPostales.setSituacionPostalEs88(situacionEntregaWs.getDescripcionCastellano());
			notifPostales.setSituacionPostalEu88(situacionEntregaWs.getDescripcionEuskera());
			notifPostales.setFechaSituacion88(situacionDateFormatter.parse(situacionEntregaWs.getFechaSituacion()));
			// Calcular si es un estado final
			Integer finalizada = Constantes.VAL_NUM_NO;
			// miramos si su descripcion tiene el literal "(Final)" para saber si esta en un
			// estado final
			if (situacionEntregaWs.getDescripcionCastellano().contains("(Final)")) {
				finalizada = Constantes.VAL_NUM_SI;
			}
			notifPostales.setFinalizada88(finalizada);
			return notifPostales;
		} catch (ParseException e) {
			throw new Ad74aWebserviceException(e.getMessage());
		}
	}

	@Override
	public File obtenerPruebaEntregaEnvio(NotifPostales notifPostales) throws IOException {

		IdentTramitadorWs identTramitadorWs = new IdentTramitadorWs();
		identTramitadorWs.setId(notifPostales.getIdEnvioPostal88());
		identTramitadorWs.setUidPuestoTramitador(notifPostales.getPostalInboxId88());
		ZipEnvioWs zipEnvioWs = aa66Notificaciones.pruebaEntregaEnvio(identTramitadorWs);

		if (zipEnvioWs != null) {
			if (zipEnvioWs.getIdEnvio() == 0 && zipEnvioWs.getError() == null) {

				throw new Ad74aException("error.download.pee");
			} else if (zipEnvioWs.getError() != null) {
				// Se trata de un error controlado del WS

				throw new Ad74aException(com.ejie.ad74a.constantes.Constantes.ES
						.equalsIgnoreCase(LocaleContextHolder.getLocale().getLanguage())
								? zipEnvioWs.getError().getDescripcionCastellano()
								: zipEnvioWs.getError().getDescripcionEuskera());
			}
		}

		List<File> files = extractFilesFromZipEnvio(zipEnvioWs);

		return files.get(0);
	}

	/*
	 * Extrae del objeto {@link ZipEnvioWs} los archivos que incluye.
	 */
	private List<File> extractFilesFromZipEnvio(ZipEnvioWs zipEnvioWs) throws IOException {
		List<File> result = new ArrayList<>();
		if (zipEnvioWs == null || CollectionUtils.isEmpty(zipEnvioWs.getFicheroZip())) {
			return result;
		}
		// Crear el directorio temporal si no existe
		String rutaTemp = this.appConfiguration.getProperty(RUTA_DATOS_TEMPORAL);
		File tmpDir = new File(rutaTemp);
		if (!tmpDir.exists()) {
			tmpDir.mkdirs();
		}
		File fileTmp;
		ZipEntry entry = null;
		String[] nameAndExt;
		for (byte[] bytes : zipEnvioWs.getFicheroZip()) {
			// Por cada zip que venga en la respuesta "ZipEnvioWs":
			try (InputStream is = new ByteArrayInputStream(bytes)) {
				ZipInputStream zis = new ZipInputStream(is);
				// Por cada fichero que venga dentro de cada zip:
				while ((entry = zis.getNextEntry()) != null) {
					// Separar nombre y extension
					nameAndExt = entry.getName().split("\\.(?=[^\\.]+$)");
					// Crear fichero temporal
					fileTmp = File.createTempFile(nameAndExt[0] + "_",
							nameAndExt.length > 1 ? ("." + nameAndExt[1]) : "",
							new File(this.appConfiguration.getProperty(RUTA_DATOS_TEMPORAL)));
					try (OutputStream os = new FileOutputStream(fileTmp)) {
						// Copiar datos
						IOUtils.copy(zis, os);
					} finally {
						zis.closeEntry();
					}
					result.add(fileTmp);
				}
			}
		}

		return result;
	}

}