package com.ejie.ad74a.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ejie.ad74a.dao.NotifPostalesDao;
import com.ejie.ad74a.exceptions.Ad74aException;
import com.ejie.ad74a.exceptions.Ad74aWebserviceException;
import com.ejie.ad74a.model.NotifPostales;
import com.ejie.ad74a.service.interoperabilidad.RemesasService;
import com.ejie.ad74a.util.comun.UtilLiterales;
import com.ejie.ad74a.util.constantes.Constantes;
import com.ejie.ad74a.util.constantes.TipoNotificacionComunicacionEnum;
import com.ejie.ad74a.util.pif.Ad74aPIFws;
import com.ejie.ad74a.ws.client.dokusi.service.DokusiService;
import com.ejie.ad74a.ws.client.platea.notificaciones.TAttachmentSend;
import com.ejie.ad74a.ws.client.platea.notificaciones.TDocField;
import com.ejie.ad74a.ws.client.platea.notificaciones.TDocFieldList;
import com.ejie.ad74a.ws.client.platea.notificaciones.TNotPostal;
import com.ejie.ad74a.ws.client.platea.notificaciones.TNotPostalList;
import com.ejie.ad74a.ws.client.platea.notificaciones.TNotification;
import com.ejie.ad74a.ws.client.platea.notificaciones.TNotificationFilter;
import com.ejie.ad74a.ws.client.platea.notificaciones.TStringList;
import com.ejie.ad74a.ws.client.platea.service.PlateaService;
import com.ejie.ad74a.ws.client.platea.tareasTramitacion.TBoolean;
import com.ejie.ad74a.ws.client.platea.tareasTramitacion.TMessage;
import com.ejie.ad74a.ws.client.platea.tareasTramitacion.TMessageList;
import com.ejie.ad74a.ws.client.platea.tareasTramitacion.TMessageStatus;
import com.ejie.ad74a.ws.client.platea.tareasTramitacion.TMessageTipology;
import com.ejie.ad74a.ws.client.platea.tareasTramitacion.TPersonIDName;
import com.ejie.t65.jaxb.beans.fsd.T65ContentListType;
import com.ejie.t65.jaxb.beans.fsd.T65ContentType;
import com.ejie.t65.jaxb.beans.fsd.T65DocumentListType;
import com.ejie.t65.jaxb.beans.fsd.T65DocumentType;
import com.ejie.x38.dto.TableRequestDto;
import com.ejie.x38.dto.TableResponseDto;
import com.ejie.x38.dto.TableRowDto;

/**
 * NotifPostalesServiceImpl generated by UDA, 09-ene-2024 16:08:21.
 * 
 * @author UDA
 */

@Service(value = "notifPostalesService")
public class NotifPostalesServiceImpl implements NotifPostalesService {

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

	@Autowired
	private NotifPostalesDao notifPostalesDao;

	@Autowired
	private PlateaService plateaService;

	@Autowired
	private DokusiService dokusiService;

	@Autowired
	private RemesasService remesasService;

	@Autowired
	private Properties appConfiguration;

	@Autowired
	private ReloadableResourceBundleMessageSource messageSource;

	private DateFormat dfListadoCorrespondenciaUrl = new SimpleDateFormat("ddMMyyyy'0000'");

	// Sin limite de tiempo por transaccion
	@Override
	public List<NotifPostales> updateMultipleSituacionEnvio(List<NotifPostales> notifPostalesList) {
		if (CollectionUtils.isEmpty(notifPostalesList)) {
			return notifPostalesList;
		}
		try {
			// Por cada elemento se recupera su situación en AA66 y se actualiza (solo si es
			// una notificacion y no se encuentra en un estado FINAL)
			List<NotifPostales> itemsActualizar = new ArrayList<>();
			for (NotifPostales item : notifPostalesList) {
				if (TipoNotificacionComunicacionEnum.NOTIFICACION.getValor().equalsIgnoreCase(item.getTipoNot88())
						&& !com.ejie.ad74a.util.constantes.Constantes.VAL_NUM_SI.equals(item.getFinalizada88())) {
					remesasService.obtenerSituacionEnvio(item);
					itemsActualizar.add(item);
				}
			}
			// Si no han ocurrido errores, se actualizan las situaciones en bbdd
			if (CollectionUtils.isNotEmpty(itemsActualizar)) {
				notifPostalesDao.updateMultiple(itemsActualizar);
			}
		} catch (Ad74aWebserviceException e) {
			throw new Ad74aException(e, e.getLocalizedMessage());
		}
		return notifPostalesList;
	}

	/**
	 * Inserts a single row in the NotifPostales table.
	 *
	 * @param notifPostales NotifPostales
	 * @return NotifPostales
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public NotifPostales add(NotifPostales notifPostales) {
		return this.notifPostalesDao.add(notifPostales);
	}

	/**
	 * Updates a single row in the NotifPostales table.
	 *
	 * @param notifPostales NotifPostales
	 * @return NotifPostales
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public NotifPostales update(NotifPostales notifPostales) {
		return this.notifPostalesDao.update(notifPostales);
	}

	/**
	 * Finds a single row in the NotifPostales table.
	 *
	 * @param notifPostales NotifPostales
	 * @return NotifPostales
	 */
	@Override
	public NotifPostales find(NotifPostales notifPostales) {
		return this.notifPostalesDao.find(notifPostales);
	}

	/**
	 * Deletes a single row in the NotifPostales table.
	 *
	 * @param notifPostales NotifPostales
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void remove(NotifPostales notifPostales) {
		this.notifPostalesDao.remove(notifPostales);
	}

	/**
	 * Finds rows in the NotifPostales table using like.
	 *
	 * @param notifPostales   NotifPostales
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 * @return List<NotifPostales>
	 */
	@Override
	public List<NotifPostales> findAllLike(NotifPostales notifPostales, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return this.notifPostalesDao.findAllLike(notifPostales, tableRequestDto, startsWith);
	}

	/*
	 * OPERACIONES RUP_TABLE
	 */

	/**
	 * Removes multiple rows from the NotifPostales table.
	 *
	 * @param filterNotifPostales NotifPostales
	 * @param tableRequestDto     TableRequestDto
	 * @param startsWith          Boolean
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void removeMultiple(NotifPostales filterNotifPostales, TableRequestDto tableRequestDto, Boolean startsWith) {
		this.notifPostalesDao.removeMultiple(filterNotifPostales, tableRequestDto, startsWith);
	}

	/**
	 * Filter method in the NotifPostales table.
	 *
	 * @param filterNotifPostales NotifPostales
	 * @param tableRequestDto     TableRequestDto
	 * @param startsWith          Boolean
	 * @return TableResponseDto<NotifPostales>
	 */
	@Override
	public TableResponseDto<NotifPostales> filter(NotifPostales filterNotifPostales, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		List<NotifPostales> listaNotifPostales = this.notifPostalesDao.findAllLike(filterNotifPostales, tableRequestDto,
				false);
		Long recordNum = this.notifPostalesDao
				.findAllLikeCount(filterNotifPostales != null ? filterNotifPostales : new NotifPostales(), false);

		TableResponseDto<NotifPostales> tableResponseDto = new TableResponseDto<>(tableRequestDto, recordNum,
				listaNotifPostales);

		if (tableRequestDto.getMultiselection().getSelectedIds() != null) {
			List<TableRowDto<NotifPostales>> reorderSelection = this.notifPostalesDao
					.reorderSelection(filterNotifPostales, tableRequestDto, startsWith);
			tableResponseDto.setReorderedSelection(reorderSelection);
			tableResponseDto.addAdditionalParam("reorderedSelection", reorderSelection);
			tableResponseDto.addAdditionalParam("selectedAll", tableRequestDto.getMultiselection().getSelectedAll());
		}
		if (tableRequestDto.getSeeker().getSelectedIds() != null) {
			tableRequestDto.setMultiselection(tableRequestDto.getSeeker());
			List<TableRowDto<NotifPostales>> reorderSeeker = this.notifPostalesDao.reorderSelection(filterNotifPostales,
					tableRequestDto, startsWith);
			tableResponseDto.setReorderedSeeker(reorderSeeker);
			tableResponseDto.addAdditionalParam("reorderedSeeker", reorderSeeker);
		}
		return tableResponseDto;
	}

	/**
	 * Reorder the selection made in NotifPostales table.
	 *
	 * @param filterNotifPostales NotifPostales
	 * @param tableRequestDto     TableRequestDto
	 * @param startsWith          Boolean
	 * @return Object
	 */
	@Override
	public Object reorderSelection(NotifPostales filterNotifPostales, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return this.notifPostalesDao.reorderSelection(filterNotifPostales, tableRequestDto, startsWith);
	}

	/**
	 * Exporta Datos al clipBoard
	 *
	 */
	@Override
	public List<NotifPostales> getMultiple(NotifPostales filterNotifPostales, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return this.notifPostalesDao.getMultiple(filterNotifPostales, tableRequestDto, startsWith);
	}

	@Override
	public List<NotifPostales> getDocumentsFromPlatea(List<NotifPostales> notifPostalesList, boolean getContents) {
		List<NotifPostales> resultList = new ArrayList<>();
		final int maxElementsPerCall = 10;
		int counter = 0;
		TNotificationFilter filter = resetNotificationFilter();
		List<NotifPostales> listNotifAux = new ArrayList<>();
		NotifPostales notifAux;
		Iterator<NotifPostales> it = notifPostalesList.iterator();
		// Recuperar documentos de las notificaciones
		while (it.hasNext()) {
			notifAux = it.next();
			listNotifAux.add(notifAux);
			counter++;
			// Se agrega al filtro la notificacion
			filter.getNotificationIDList().getItem().add(notifAux.getTareasTramite().getIdNotificacion11());
			if (counter >= maxElementsPerCall || !it.hasNext()) {
				obtenerDocsNotif(filter, listNotifAux, getContents);
				resultList.addAll(listNotifAux);
				// Resetear filtro
				counter = 0;
				listNotifAux = new ArrayList<>();
				filter = resetNotificationFilter();
			}
		}
		return resultList;
	}

	/*
	 * Obtiene los documentos de las notificaciones indicadas en la lista y en el
	 * filtro
	 */
	private void obtenerDocsNotif(TNotificationFilter filter, List<NotifPostales> listNotifAux, boolean getContents) {
		List<TNotification> listResult;
		// Si se ha llegado al cupo de elementos por llamada, o es el ultimo elemento,
		// realizar llamada a platea
		List<String> strOidList = new ArrayList<>();
		listResult = plateaService.searchNotifications(filter);
		for (TNotification t : listResult) {
			for (NotifPostales n : listNotifAux) {
				if (n.getTareasTramite().getIdNotificacion11().equalsIgnoreCase(t.getNotificationID())) {
					// Es la misma notificacion, informamos aqui los datos de los documentos
					n.setDocNotifPlatea(t.getDocument());
					strOidList.add(t.getDocument().getDokusiID());
					break;
				}
			}
		}
		// (por si hay notificaciones repetidas, se quitan)
		Iterator<NotifPostales> i = listNotifAux.iterator();
		while (i.hasNext()) {
			NotifPostales n = i.next();
			if (n.getDocNotifPlatea() == null) {
				// Si el elemento no tiene datos de platea se quita
				i.remove();
			}
		}
		obtenerContenidoDoc(listNotifAux, getContents, strOidList);
	}

	/*
	 * Obtiene el contenido de los documentos seleccionados
	 */
	private void obtenerContenidoDoc(List<NotifPostales> listNotifAux, boolean getContents, List<String> strOidList) {
		if (getContents) {
			final String pifPath = this.appConfiguration.getProperty("webservice.dokusi.pif.dir");
			T65ContentListType t65BContentList = T65ContentListType.Factory.newInstance();
			T65ContentType t65BContent = null;
			for (int i = 0; i < listNotifAux.size(); i++) {
				t65BContent = t65BContentList.addNewContent();
				// t65BContentType.addNewFormat().setName("pdf"); No se indica formato, por
				// defecto
				// recupera pdf, si hay.
				t65BContent.setPifId(pifPath); // Carpeta pif donde dejar los documentos
			}
			T65DocumentListType documentList = dokusiService.retrieveDocuments(strOidList.toArray(new String[] {}),
					t65BContentList, new String[0]);
			for (T65DocumentType doc : documentList.getDocumentArray()) {
				for (NotifPostales n : listNotifAux) {
					if (n.getDocNotifPlatea().getDokusiID().equalsIgnoreCase(doc.getDocumentID().getId())) {
						n.setPifDocumentPath(doc.getContent().getPifId());
						n.setPifDocumentSize(doc.getContent().getLenght());
						break;
					}
				}
			}
		}
	}

	/*
	 * Crea nuevo objeto de filtro inicializado
	 */
	private TNotificationFilter resetNotificationFilter() {
		TNotificationFilter filter = new TNotificationFilter();
		filter.setDocFieldList(new TDocFieldList());
		filter.getDocFieldList().getItem().add(TDocField.DOC_INFO);
		filter.setNotificationIDList(new TStringList());
		return filter;
	}

	@Override
	public Map<String, Object> obtenerParametrosListadoCorrespondenciaPorFechas(NotifPostales filter)
			throws IOException {
		// Obtener todos los usuarios diferentes de las notificaciones que coincidan con
		// los filtros.
		filter.setTipoNot88(TipoNotificacionComunicacionEnum.NOTIFICACION.getValor());
		filter.setFinalizada88(Constantes.VAL_NUM_NO);
		List<NotifPostales> notifPostalesList = this.notifPostalesDao.findAllLike(filter,
				new TableRequestDto(null, null, "POSTALINBOXID88,", "ASC"), false);
		if (CollectionUtils.isEmpty(notifPostalesList)) {
			return null;
		}
		String url = appConfiguration.getProperty("url.aa66.listadocorrespondencia");
		String fDesde = dfListadoCorrespondenciaUrl.format(filter.getFechaRegistroInicio());
		Calendar cFin = Calendar.getInstance(LocaleContextHolder.getLocale());
		cFin.setTime(filter.getFechaRegistroFin());
		cFin.add(Calendar.DATE, 1);// Siguiente día a las 00:00
		String fHasta = dfListadoCorrespondenciaUrl.format(cFin.getTime());
		String postalInboxIdPrev = null;
		Map<String, Object> resultado = new HashMap<>();
		List<String> urls = new ArrayList<>();
		for (NotifPostales item : notifPostalesList) {
			// Por cada notificacion obtenida, si es de un usuario distinto (la busqueda
			// esta ordenada por usuario), se pide su listado a a66
			if (postalInboxIdPrev == null || !postalInboxIdPrev.equalsIgnoreCase(item.getPostalInboxId88())) {
				postalInboxIdPrev = item.getPostalInboxId88();// Actualiza buzon de usuario actual en el bucle
				urls.add(MessageFormat.format(url, postalInboxIdPrev, fDesde, fHasta));
			}
		}
		// Se devuelve en un mapa los parametros necesarios para que el usuario invoque
		// al servlet de aa66 desde el navegador
		resultado.put(NotifPostalesService.ParametrosListadoCorrespondeciaFechas.URL_LIST.name(), urls);
		resultado.put(NotifPostalesService.ParametrosListadoCorrespondeciaFechas.FICHERO_NOMBRE.name(),
				UtilLiterales.getLiteralMessageWithParams(messageSource, "notifpostales.ficheroListadoCorrespondencia",
						new Object[] { fDesde, fHasta }));
		return resultado;
	}

	@Override
	public File getAdjuntoRemitoFromPlatea(List<NotifPostales> notifPostalesList)
			throws IOException, Ad74aWebserviceException {

		TAttachmentSend attachmentSend = new TAttachmentSend();
		attachmentSend.setNotPostalList(new TNotPostalList());
		for (NotifPostales item : notifPostalesList) {
			TNotPostal notPostal = new TNotPostal();
			notPostal.setNotificationID(item.getTareasTramite().getIdNotificacion11());
			attachmentSend.getNotPostalList().getNotPostal().add(notPostal);
		}
		attachmentSend.setDirPif(this.appConfiguration.getProperty("ruta.pif.temporal"));
		String pifResultPath = this.plateaService.getAttachmentSend(attachmentSend);
		String rutaTemp = this.appConfiguration.getProperty(RUTA_DATOS_TEMPORAL);
		File resultFile = File.createTempFile("adjunto_remito", ".pdf", new File(rutaTemp));
		try (OutputStream outputStream = new FileOutputStream(resultFile)) {
			IOUtils.copy(Ad74aPIFws.getInstance().downloadDocument(pifResultPath), outputStream);
		}
		return resultFile;
	}

	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
	public NotifPostales retramitarNotificacion(NotifPostales notifPostales) {
		// Recuperar datos de la notificacion que se quiere retramitar
		NotifPostales filtro = new NotifPostales(notifPostales.getIdNotifPostal88());
		NotifPostales item = notifPostalesDao.find(filtro);
		if (item.getRetramitar() != null
				&& item.getRetramitar().equals(Constantes.NotifPostalesRetramitar.RETRAMITANDO)) {
			// Si la notificacion ya esta retramitandose, se devuelve un error
			throw new Ad74aException("notifpostales.error.yaestaretramitandose");
		}
		// Actualizar estado de la notificacion, para poder ser recuperada cuando llegue
		// el evento de tarea sugerida
		item.setRetramitar(Constantes.NotifPostalesRetramitar.RETRAMITANDO);
		notifPostalesDao.update(item);
		String taskGUID = item.getTareasTramite().getTaskguid11();
		String folderID = item.getTareasTramite().getTramitesExpediente().getExpediente().getFolderId00();
		// Recuperar datos de la tarea en PLATEA que ha creado la notificación
		TMessageList tMessageList = plateaService.getGATTask(folderID, TMessageStatus.FINISHED,
				TMessageTipology.TRAMITACION);
		// De la lista de tareas devuelta cogemos la que coincida con el taskGUID que
		// queremos
		TMessage tarea = null;
		if (tMessageList.getMessage() != null) {
			for (TMessage tMessage : tMessageList.getMessage()) {
				if (tMessage.getTaskGUID().equalsIgnoreCase(taskGUID)) {
					tarea = tMessage;
					break;
				}
			}
		}
		if (tarea == null) {
			// Si no se han encontrado tareas, se devuelve una excepcion
			throw new Ad74aException("notifpostales.error.noexistetarearetramitar");
		}
		TPersonIDName senderId = new TPersonIDName();
		senderId.setID(plateaService.getMessageParameter(tarea, "senderId"));
		senderId.setName(plateaService.getMessageParameter(tarea, "senderName"));
		com.ejie.ad74a.ws.client.platea.tareasTramitacion.TStringList mailBoxStringList = new com.ejie.ad74a.ws.client.platea.tareasTramitacion.TStringList();
		mailBoxStringList.getItem().add(plateaService.getMessageParameter(tarea, "MAILBOX"));
		// Sugerir de nuevo la misma tarea de notificación recuperada
		TBoolean result = plateaService.prepareNewTask(plateaService.getMessageParameter(tarea, "procedureID"),
				folderID, plateaService.getMessageParameter(tarea, "proceedingID"),
				plateaService.getMessageParameter(tarea, "taskID"), senderId, mailBoxStringList);
		if (result == null || TBoolean.FALSE.equals(result)) {
			// Algo no ha ido bien al sugerir la tarea, lanzar excepcion
			throw new Ad74aException("notifpostales.error.sugerirtarearetramitar");
		}
		/*- HAY QUE VER LA MANERA DE QUE ESTA TAREA SUGERIDA SE TRATE DIFERENTE QUE EL RESTO DE NOTIFICACIONES.
		 * Las demás tareas de notificaciones se sugieren automáticamente por flujo, y al ejecutarlas recuperan 
		 * determinados datos de la tarea anterior (APORDOCU) que las ha sugerido:
		 * 
		 * La solución es comprobar la lista de documentos que devuelve el prepareTask de la tarea. Si la trae 
		 * informada, es que viene sugerida del flujo desde un APORDOCU. Sino, se trata de una retramitación.
		 */
		return item;
	}

}
