/*
* Copyright 2021 E.J.I.E., S.A.
*
* Licencia con arreglo a la EUPL, Versión 1.1 exclusivamente (la «Licencia»);
* Solo podrá usarse esta obra si se respeta la Licencia.
* Puede obtenerse una copia de la Licencia en
*
* http://ec.europa.eu/idabc/eupl.html
*
* Salvo cuando lo exija la legislación aplicable o se acuerde por escrito,
* el programa distribuido con arreglo a la Licencia se distribuye «TAL CUAL»,
* SIN GARANT�?AS NI CONDICIONES DE NINGÚN TIPO, ni expresas ni implícitas.
* Véase la Licencia en el idioma concreto que rige los permisos y limitaciones
* que establece la Licencia.
*/
package com.ejie.ad74a.service;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.odftoolkit.odfdom.doc.OdfSpreadsheetDocument;
import org.odftoolkit.odfdom.doc.table.OdfTable;
import org.odftoolkit.odfdom.doc.table.OdfTableRow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ejie.ad74a.constantes.Constantes;
import com.ejie.ad74a.dao.ProcesosDao;
import com.ejie.ad74a.exceptions.Ad74aException;
import com.ejie.ad74a.model.FacturacionAyuntamientos;
import com.ejie.ad74a.model.Mensualidades;
import com.ejie.ad74a.model.Pagos;
import com.ejie.ad74a.model.Procesos;
import com.ejie.ad74a.util.comun.UtilSeguridad;
import com.ejie.ad74a.util.comun.Utilidades;
import com.ejie.ad74a.util.constantes.EstadosProcesosBatchEnum;
import com.ejie.ad74a.util.constantes.ProcesosBatchEnum;
import com.ejie.x38.dto.TableRequestDto;
import com.ejie.x38.dto.TableResponseDto;
import com.ejie.x38.dto.TableRowDto;
import com.ejie.x38.util.DateTimeManager;
import com.lowagie.text.Document;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;

import o75d.exe.O75API;
import o75d.exe.O75Peticion;

/**
 * ProcesosServiceImpl generated by UDA, 07-oct-2024 11:55:26.
 * 
 * @author UDA
 */

@Service(value = "procesosService")
public class ProcesosServiceImpl implements ProcesosService {

	private static final Logger logger = LoggerFactory.getLogger(ProcesosService.class);

	@Autowired
	private ProcesosDao procesosDao;

	@Autowired
	private Properties appConfiguration;

	@Autowired
	private ReloadableResourceBundleMessageSource appMessageSource;

	/**
	 * Inserts a single row in the Procesos table.
	 *
	 * @param procesos Procesos
	 *
	 * @return Procesos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Procesos add(Procesos procesos) {
		return this.procesosDao.add(procesos);
	}

	/**
	 * Updates a single row in the Procesos table.
	 *
	 * @param procesos Procesos
	 *
	 * @return Procesos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Procesos update(Procesos procesos) {
		return this.procesosDao.update(procesos);
	}

	/**
	 * Finds a single row in the Procesos table.
	 *
	 * @param procesos Procesos
	 *
	 * @return Procesos
	 */
	public Procesos find(Procesos procesos) {
		return (Procesos) this.procesosDao.find(procesos);
	}

	/**
	 * Deletes a single row in the Procesos table.
	 *
	 * @param procesos Procesos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public void remove(Procesos procesos) {
		this.procesosDao.remove(procesos);
	}

	/**
	 * Finds a list of rows in the Procesos table.
	 *
	 * @param procesos        Procesos
	 * @param tableRequestDto TableRequestDto
	 *
	 * @return List<Procesos>
	 */
	public List<Procesos> findAll(Procesos procesos, TableRequestDto tableRequestDto) {
		return (List<Procesos>) this.procesosDao.findAll(procesos, tableRequestDto);
	}

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

	/*
	 * OPERACIONES RUP_TABLE
	 */

	/**
	 * Removes rows from the Procesos table.
	 *
	 * @param tableRequestDto TableRequestDto
	 */
	@Transactional(rollbackFor = Throwable.class)
	public void removeMultiple(TableRequestDto tableRequestDto) {
		this.procesosDao.removeMultiple(tableRequestDto);
	}

	/**
	 * Filter method in the Procesos table.
	 *
	 * @param filterProcesos  Procesos
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 *
	 * @return TableResourceResponseDto<Procesos>
	 */
	public TableResponseDto<Procesos> filter(Procesos filterProcesos, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		List<Procesos> listaProcesos = this.procesosDao.findAllLike(filterProcesos, tableRequestDto, false);
		Long recordNum = this.procesosDao.findAllLikeCount(filterProcesos != null ? filterProcesos : new Procesos(),
				false);
		TableResponseDto<Procesos> tableResponseDto = new TableResponseDto<Procesos>(tableRequestDto, recordNum,
				listaProcesos);

		if (tableRequestDto.getMultiselection().getSelectedIds() != null
				&& !tableRequestDto.getMultiselection().getSelectedIds().isEmpty()) {
			List<TableRowDto<Procesos>> reorderSelection = this.procesosDao.reorderSelection(filterProcesos,
					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<Procesos>> reorderSeeker = this.procesosDao.reorderSelection(filterProcesos,
					tableRequestDto, startsWith);
			tableResponseDto.setReorderedSeeker(reorderSeeker);
			tableResponseDto.addAdditionalParam("reorderedSeeker", reorderSeeker);
		}
		return tableResponseDto;
	}

	/**
	 * Searches rows in the Procesos table.
	 *
	 * @param filterProcesos  Procesos
	 * @param searchProcesos  Procesos
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 *
	 * @return List<TableRowDto<Procesos>>
	 */
	public List<TableRowDto<Procesos>> search(Procesos filterProcesos, Procesos searchProcesos,
			TableRequestDto tableRequestDto, Boolean startsWith) {
		return this.procesosDao.search(filterProcesos, searchProcesos, tableRequestDto, startsWith);
	}

	/**
	 * Reorder the selection made in Procesos table.
	 *
	 * @param filterProcesos  Procesos
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 *
	 * @return Object
	 */
	public Object reorderSelection(Procesos filterProcesos, TableRequestDto tableRequestDto, Boolean startsWith) {
		return this.procesosDao.reorderSelection(filterProcesos, tableRequestDto, startsWith);
	}

	@Transactional(rollbackFor = Throwable.class)
	public List<Procesos> getMultiple(Procesos filterProcesos, TableRequestDto tableRequestDto, Boolean startsWith) {
		return this.procesosDao.getMultiple(filterProcesos, tableRequestDto, startsWith);
	}

	/**
	 * Devuelve un fichero en el formato deseado que contiene los datos exportados
	 * de la tabla.
	 *
	 * @param filterProcesos  Procesos
	 * @param columns         String[]
	 * @param columnsName     String[]
	 * @param fileName        String
	 * @param sheetTitle      String
	 * @param reportsParams   ArrayList<?>
	 * @param tableRequestDto TableRequestDto
	 * @param request         HttpServletRequest
	 * @param response        HttpServletResponse
	 */
	@Override
	public void generateReport(Procesos filterProcesos, String[] columns, String[] columnsName, String fileName,
			String sheetTitle, ArrayList<?> reportsParams, TableRequestDto tableRequestDto, HttpServletRequest request,
			HttpServletResponse response) {
		// Accede a la DB para recuperar datos
		List<Procesos> filteredData = getDataForReports(filterProcesos, tableRequestDto);
		String extension = null;

		// Comprobar si las siguientes variables estan vacias, en caso de estarlo se las
		// asigna un valor generico
		fileName = (fileName != null && !fileName.isEmpty()) ? fileName : "report";
		sheetTitle = (sheetTitle != null && !sheetTitle.isEmpty()) ? sheetTitle : Procesos.class.getSimpleName();

		// Obtener el formato de fecha especifico del locale
		SimpleDateFormat formatter = DateTimeManager.getDateTimeFormat(request.getLocale());

		// Cuando no se definen columnas porque se quieren obtener todas
		if (columns == null) {
			Field[] fields = Procesos.class.getDeclaredFields();
			ArrayList<String> tempColumns = new ArrayList<String>();

			for (int i = 0; i < fields.length; i++) {
				try {
					String methodName = fields[i].getName();
					methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
					Procesos.class.getMethod("get" + methodName);
					tempColumns.add(fields[i].getName());
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				}

			}
			columns = tempColumns.toArray(new String[0]);
		}

		// Si no se definen los nombres de las columnas se dejan las definidas por
		// defecto
		if (columnsName == null) {
			columnsName = columns;
		}

		String servletPath = request.getServletPath();
		String reportType = null;
		if (servletPath.contains("/") && (servletPath.lastIndexOf("/") + 1 != servletPath.length())) {
			reportType = servletPath.substring(servletPath.lastIndexOf("/") + 1, servletPath.length());
		} else {
			reportType = servletPath.substring(0, servletPath.length());
		}

		if (reportType.equals("xlsReport")) {
			extension = ".xls";
			generateExcelReport(filteredData, columns, columnsName, fileName, sheetTitle, extension, formatter,
					response);
		} else if (reportType.equals("xlsxReport")) {
			extension = ".xlsx";
			generateExcelReport(filteredData, columns, columnsName, fileName, sheetTitle, extension, formatter,
					response);
		} else if (reportType.equals("pdfReport")) {
			extension = ".pdf";
			generatePDFReport(filteredData, columns, columnsName, fileName, response);
		} else if (reportType.equals("odsReport")) {
			extension = ".ods";
			generateODSReport(filteredData, columns, columnsName, fileName, sheetTitle, response);
		} else if (reportType.equals("csvReport")) {
			extension = ".csv";
			// Obtener idioma
			String language = request.getLocale().getLanguage();
			generateCSVReport(filteredData, columns, columnsName, fileName, sheetTitle, language, response);
		}

	}

	/**
	 * Devuelve un fichero excel que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Procesos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param extension    String
	 * @param formatter    SimpleDateFormat
	 * @param response     HttpServletResponse
	 */
	private void generateExcelReport(List<Procesos> filteredData, String[] columns, String[] columnsName,
			String fileName, String sheetTitle, String extension, SimpleDateFormat formatter,
			HttpServletResponse response) {
		try {
			// Creacion del Excel
			Workbook workbook = null;
			if (extension == ".xlsx") {
				workbook = new XSSFWorkbook();
				response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
			} else {
				workbook = new HSSFWorkbook();
				response.setContentType("application/vnd.ms-excel");
			}

			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + extension + "");

			// Creacion de una hoja y asignacion de su nombre
			Sheet sheet = workbook.createSheet(sheetTitle);

			// Se crea una fuente para estilizar las cabeceras
			Font headerFont = workbook.createFont();
			headerFont.setBold(true);
			headerFont.setFontHeightInPoints((short) 12);

			// Se crea un CellStyle con la fuente
			CellStyle headerCellStyle = workbook.createCellStyle();
			headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
			headerCellStyle.setFont(headerFont);

			// Inicializar contador de filas
			int rowNumber = 0;

			// Se crea la fila para insertar los titulos de las columnas
			Row row = sheet.createRow(rowNumber++);

			// Añadir titulos
			for (int i = 0; i < columnsName.length; i++) {
				Cell cell = row.createCell(i);
				cell.setCellValue(columnsName[i]);
				cell.setCellStyle(headerCellStyle);
			}

			// CreationHelper ayudara a mantener la compatibilidad del DataFormat tanto si
			// se crea un .xls como un .xlsx
			CreationHelper createHelper = workbook.getCreationHelper();

			// Se crea un CellStyle para añadir el formateador de fechas
			CellStyle dateCellStyle = workbook.createCellStyle();
			dateCellStyle.setDataFormat(createHelper.createDataFormat().getFormat(formatter.toPattern()));

			// Añadir datos
			for (Procesos rowProcesos : filteredData) {
				int cellNumber = 0;
				row = sheet.createRow(rowNumber++);

				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					Cell cellProcesos = row.createCell(cellNumber++);
					cellProcesos.setCellValue(getCellValue(column, rowProcesos));
				}
			}

			// Se adapta el ancho de las columnas al contenido
			for (int i = 0; i < columns.length; i++) {
				sheet.autoSizeColumn(i);
			}

			// Se añade el fichero excel al response
			workbook.write(response.getOutputStream());
			workbook.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve un fichero pdf que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Procesos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param response     HttpServletResponse
	 */
	private void generatePDFReport(List<Procesos> filteredData, String[] columns, String[] columnsName, String fileName,
			HttpServletResponse response) {
		try {
			// Se añade el fichero excel al response y se añade el contenido
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".pdf");
			response.setContentType("application/pdf");

			Document document = new Document();
			// Se añade el fichero pdf al response
			PdfWriter.getInstance(document, response.getOutputStream());

			document.open();

			PdfPTable table = new PdfPTable(columns.length);

			for (String column : columnsName) {
				PdfPCell header = new PdfPCell();
				header.setBorderWidth(2);
				header.setPhrase(new Phrase(column));
				table.addCell(header);
			}

			// Añadir datos
			for (Procesos rowProcesos : filteredData) {
				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					table.addCell(getCellValue(column, rowProcesos));
				}
			}

			document.add(table);
			document.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve un fichero ods que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Procesos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param response     HttpServletResponse
	 */
	private void generateODSReport(List<Procesos> filteredData, String[] columns, String[] columnsName, String fileName,
			String sheetTitle, HttpServletResponse response) {
		try {
			// Se añade el fichero ods al response y se añade el contenido
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".ods");
			response.setContentType("application/vnd.oasis.opendocument.spreadsheet");

			OdfSpreadsheetDocument ods = OdfSpreadsheetDocument.newSpreadsheetDocument();
			ods.getOrCreateDocumentStyles();

			// Hay que eliminar la hoja que se genera por defecto
			ods.getTableByName("Sheet1").remove();

			// Hoja nueva
			OdfTable table = OdfTable.newTable(ods, filteredData.size() + 1, columns.length);
			table.setTableName(sheetTitle);

			// Inicializar contador de filas
			int rowNumber = 0;

			// Cabeceras
			OdfTableRow row = table.getRowByIndex(rowNumber++);
			for (int i = 0; i < columnsName.length; i++) {
				row.getCellByIndex(i).setStringValue(columnsName[i]);
			}

			// Añadir datos
			for (Procesos rowProcesos : filteredData) {
				row = table.getRowByIndex(rowNumber++);
				int cellNumber = 0;

				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					row.getCellByIndex(cellNumber++).setStringValue(getCellValue(column, rowProcesos));
				}
			}

			// Se añade el fichero ods al response
			ods.save(response.getOutputStream());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve un fichero csv que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Procesos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param language     String
	 * @param response     HttpServletResponse
	 */
	private void generateCSVReport(List<Procesos> filteredData, String[] columns, String[] columnsName, String fileName,
			String sheetTitle, String language, HttpServletResponse response) {
		try {
			// Se añade el fichero excel al response y se añade el contenido
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".csv");
			response.setContentType("text/csv");

			// Separador de campos dependiendo del idioma
			String separator = ";";
			if (language.equals("en")) {
				separator = ",";
			}

			// Se añade el fichero csv al response
			OutputStream out = response.getOutputStream();
			// Añadir titulos
			boolean addTitles = true;

			// Añadir datos
			for (Procesos rowProcesos : filteredData) {
				int cellNumber = 1;
				StringBuilder columnsTitles = new StringBuilder();
				StringBuilder row = new StringBuilder();

				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					String cellValue = getCellValue(column, rowProcesos);

					if (cellNumber < columns.length) {
						if (addTitles) {
							columnsTitles.append("\"");
							columnsTitles.append(column);
							columnsTitles.append("\"");
							columnsTitles.append(separator);
						}
						row.append("\"");
						row.append(cellValue);
						row.append("\"");
						row.append(separator);
						cellNumber++;
					} else {
						if (addTitles) {
							columnsTitles.append("\"");
							columnsTitles.append(column);
							columnsTitles.append("\"\n");
						}
						row.append("\"");
						row.append(cellValue);
						row.append("\"\n");
					}
				}

				if (addTitles) {
					out.write(columnsTitles.toString().getBytes());
					addTitles = false;
				}
				out.write(row.toString().getBytes());
			}
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve los datos recuperados de la DB.
	 *
	 * @param filterProcesos  Procesos
	 * @param tableRequestDto TableRequestDto
	 *
	 * @return List<Procesos>
	 */
	public List<Procesos> getDataForReports(Procesos filterProcesos, TableRequestDto tableRequestDto) {
		if (tableRequestDto.getMultiselection().getSelectedAll()
				&& tableRequestDto.getMultiselection().getSelectedIds().isEmpty()) {
			if (filterProcesos != null) {
				return this.procesosDao.findAllLike(filterProcesos, tableRequestDto, false);
			} else {
				return this.procesosDao.findAll(new Procesos(), null);
			}
		} else {
			return this.procesosDao.getMultiple(filterProcesos, tableRequestDto, false);
		}
	}

	/**
	 * Obtiene los valores de las celdas.
	 *
	 * @param column      String
	 * @param rowProcesos Procesos
	 *
	 * @return String
	 */
	private String getCellValue(String column, Procesos rowProcesos) {
		String cellValue = "";
		try {
			cellValue = BeanUtils.getProperty(rowProcesos, column) != null ? BeanUtils.getProperty(rowProcesos, column)
					: "";
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		return cellValue;
	}

	@Override
	public String lanzarPeticion_anadirPagoPrestador_O75(Procesos proceso, Pagos pago, HttpServletRequest request)
			throws Exception {

		try {
			O75Peticion o75pet = this.prepararPeticion_A_O75();
			o75pet.SetIdShell(ProcesosBatchEnum.ANADIR_PAGO_PRESTADOR.getShell());
			o75pet.SetDescripcion(ProcesosBatchEnum.ANADIR_PAGO_PRESTADOR.getLabelDesc(new Locale("es")));
			o75pet.SetParam1("@@A@@" + pago.getAnyoPago74() + "@@" + pago.getMesPago74() + "@@"
					+ proceso.getIdProceso98() + "@@");

			Map<String, String> resultado = this.lanzarPeticion_A_O75(o75pet, request, proceso);

			if (resultado.containsKey("codError")) {
				throw new Ad74aException(resultado.get("mensaje"));
			}

			return resultado.get("mensaje");

		} catch (Exception e) {
			throw new Ad74aException(e.getMessage());
		}

	}

	@Override
	public String lanzarPeticion_recalcularPagoPrestador_O75(Procesos proceso, Pagos pago, HttpServletRequest request)
			throws Exception {

		try {
			O75Peticion o75pet = this.prepararPeticion_A_O75();
			o75pet.SetIdShell(ProcesosBatchEnum.RECALCULAR_PAGO_PRESTADOR.getShell());
			o75pet.SetDescripcion(ProcesosBatchEnum.RECALCULAR_PAGO_PRESTADOR.getLabelDesc(new Locale("es")));
			o75pet.SetParam1("@@R@@" + pago.getIdPago74().toString() + "@@" + proceso.getIdProceso98() + "@@");
			Map<String, String> resultado = this.lanzarPeticion_A_O75(o75pet, request, proceso);

			if (resultado.containsKey("codError")) {
				throw new Ad74aException(resultado.get("mensaje"));
			}

			return resultado.get("mensaje");

		} catch (Exception e) {
			throw new Ad74aException(e.getMessage());
		}
	}

	@Override
	public String lanzarPeticion_copagoAtyos_O75(Procesos proceso, HttpServletRequest request) throws Exception {

		try {
			O75Peticion o75pet = this.prepararPeticion_A_O75();
			o75pet.SetIdShell(ProcesosBatchEnum.COPAGO_AYTOS.getShell());
			o75pet.SetDescripcion(ProcesosBatchEnum.COPAGO_AYTOS.getLabelDesc(new Locale("es")));
			o75pet.SetParam1("@@" + proceso.getIdProceso98() + "@@");
			Map<String, String> resultado = this.lanzarPeticion_A_O75(o75pet, request, proceso);

			if (resultado.containsKey("codError")) {
				throw new Ad74aException(resultado.get("mensaje"));
			}

			return resultado.get("mensaje");

		} catch (Exception e) {
			throw new Ad74aException(e.getMessage());
		}
	}

	/**
	 * Prepara la base para la petición a la O75
	 * 
	 * @param request
	 * @return
	 */
	private O75Peticion prepararPeticion_A_O75() throws Exception {

		String buzon = (String) this.appConfiguration.getProperty("o75.buzon");
		String aplic = Constantes.COD_APLICACION.toLowerCase();

		String usuSISSA = Constantes.COD_APLICACION;
		try {
			usuSISSA = UtilSeguridad.getUsuarioConectado().getPosition();
		} catch (Exception e) {
			ProcesosServiceImpl.logger
					.info("El UserCredentials es nulo. Usamos AD74A como usuario para la petición O75");
		}

		SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		Date now = new Date();
		String fecPeticion = sdfDate.format(now);

		O75Peticion o75pet = new O75Peticion();

		try {
			o75pet.SetBuzon(buzon);
			o75pet.SetPropietario("ad74aBuzon");
			o75pet.SetAplicacion(aplic);
			o75pet.SetUsuario(usuSISSA);
			o75pet.SetFechaPeticion(fecPeticion);
			o75pet.SetRuntime(O75API.RUNTIME_SH);
			o75pet.SetTipoPeticion(O75API.TIPOPET_ROLTIPO);
			o75pet.SetTipo(O75API.TIPO_DIRECTO);
			o75pet.SetSubtipo(O75API.SUBTIPO_RELANZABLE);
			o75pet.SetPrioridad(O75API.PRIORIDAD_NORMAL);
		} catch (Exception e) {
			e.printStackTrace();
			throw new Ad74aException(
					"ProcesosServiceImpl.prepararPeticion_A_O75 - Error al preparar la petición a la O75: "
							+ e.getMessage());
		}

		return o75pet;
	}

	/**
	 * Prepara la base para la petición a la O75
	 * 
	 * @param request
	 * @return
	 */
	private Map<String, String> lanzarPeticion_A_O75(O75Peticion o75pet, HttpServletRequest request, Procesos proceso)
			throws Exception {

//		StringBuilder res = new StringBuilder(3000);
		Map<String, String> resultado = new HashMap<String, String>();

		try {

			// Descomentar al subir a otro entorno que no sea local
			O75API o75api = new O75API(request);

			// Descomentar al probar en local
//			O75API o75api = new O75API(UtilTokenXlnets.getTokenDocumentXLNets(Constantes.COD_APLICACION));

			LocalDateTime currentTimestamp = LocalDateTime.now();

			// Define a custom format for the timestamp (optional)
			DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

			// Print the current timestamp
			ProcesosServiceImpl.logger.info("Inicio llamada: " + currentTimestamp.format(formatter));
			ProcesosServiceImpl.logger.info(o75pet.GetXML());
			Integer nRet = o75api.Alta(o75pet);

			currentTimestamp = LocalDateTime.now();
			ProcesosServiceImpl.logger.info("Fin llamada: " + currentTimestamp.format(formatter));

			ProcesosServiceImpl.logger.info("Parametros enviados a O75 - \n buzon: " + o75pet.GetBuzon() //
					+ ",\n aplicacion: " + o75pet.GetAplicacion() //
					+ ",\n usuario: " + o75pet.GetUsuario() //
					+ ",\n fecha: " + o75pet.GetFechaPeticion() //
					+ ",\n runtime: " + o75pet.GetRuntime() //
					+ ",\n idShell: " + o75pet.GetIdShell()//
					+ ",\n param1: " + o75pet.GetParam1() //
					+ ",\n descipcion: " + o75pet.GetDescripcion()//
					+ ",\n tipoPet: " + o75pet.GetTipoPet()//
					+ ",\n tipo: " + o75pet.GetTipo() //
					+ ",\n subtipo: " + o75pet.GetSubtipo());

			resultado.put("nRet", nRet.toString());
			resultado.put("numPeticion", Long.toString(o75pet.GetNumeroPeticion()));

			String mensaje = "";
			if (0 == nRet) {
				String[] params = { Long.toString(o75pet.GetNumeroPeticion()) };
				mensaje = Utilidades.getLabelFromResourceWithParams(
						com.ejie.ad74a.util.constantes.Constantes.BASENAME_RESOURCE_AD74A_I18N, "procesos.o75.ok",
						request.getLocale(), params);
				proceso.setNumBatch98(o75pet.GetNumeroPeticion());
				proceso.setEstado98(EstadosProcesosBatchEnum.FINALIZADO.getId());
				// this.procesosDao.actualizarAFinalizado(proceso);
				this.procesosDao.actualizarNumBatch(proceso);

			} else {
				String[] params = { Integer.toString(o75api.GetCodigoError()), o75api.GetDec() };
				mensaje = Utilidades.getLabelFromResourceWithParams(
						com.ejie.ad74a.util.constantes.Constantes.BASENAME_RESOURCE_AD74A_I18N, "procesos.o75.nok",
						request.getLocale(), params);
				resultado.put("codError", Integer.toString(o75api.GetCodigoError()));
				resultado.put("desc", o75api.GetDec());
				proceso.setError98(o75api.GetDec());
				proceso.setEstado98(EstadosProcesosBatchEnum.ERROR.getId());
				this.procesosDao.actualizarAError(proceso);
			}

			ProcesosServiceImpl.logger.info(mensaje);
			resultado.put("mensaje", mensaje);

			return resultado;
		} catch (Exception e) {
			throw new Ad74aException("ProcesosServiceImpl.lanzarPeticion_A_O75 - Error al lanzar la petición a la O75: "
					+ e.getMessage());
		}

	}

	@Override
	public String lanzarPeticion_emitirLiquidacionAyuntamientos_O75(Procesos proceso,
			FacturacionAyuntamientos facturacionAyuntamientos, HttpServletRequest request) throws Exception {

		try {
			O75Peticion o75pet = this.prepararPeticion_A_O75();
			o75pet.SetIdShell(ProcesosBatchEnum.COPAGO_AYTOS_FACT.getShell());
			o75pet.SetDescripcion(ProcesosBatchEnum.COPAGO_AYTOS_FACT.getLabelDesc(new Locale("es")));
			o75pet.SetParam1(
					"@@" + facturacionAyuntamientos.getIdFacturacion90() + "@@" + proceso.getIdProceso98() + "@@");
			Map<String, String> resultado = this.lanzarPeticion_A_O75(o75pet, request, proceso);

			if (resultado.containsKey("codError")) {
				throw new Ad74aException(resultado.get("mensaje"));
			}

			return resultado.get("mensaje");

		} catch (Exception e) {
			throw new Ad74aException(e.getMessage());
		}

	}

	@Override
	public String lanzarPeticion_ayuntamientosExcel_O75(Procesos proceso,
			FacturacionAyuntamientos facturacionAyuntamientos, HttpServletRequest request) throws Exception {

		try {
			O75Peticion o75pet = this.prepararPeticion_A_O75();
			o75pet.SetIdShell(ProcesosBatchEnum.COPAGO_AYTOS_EXCEL.getShell());
			o75pet.SetDescripcion(ProcesosBatchEnum.COPAGO_AYTOS_EXCEL.getLabelDesc(new Locale("es")));
			o75pet.SetParam1(
					"@@" + facturacionAyuntamientos.getIdFacturacion90() + "@@" + proceso.getIdProceso98() + "@@");
			Map<String, String> resultado = this.lanzarPeticion_A_O75(o75pet, request, proceso);

			if (resultado.containsKey("codError")) {
				throw new Ad74aException(resultado.get("mensaje"));
			}

			return resultado.get("mensaje");

		} catch (Exception e) {
			throw new Ad74aException(e.getMessage());
		}

	}

	@Override
	public String lanzarPeticion_relanzarCargos_O75(Procesos proceso, HttpServletRequest request) throws Exception {

		try {
			O75Peticion o75pet = this.prepararPeticion_A_O75();
			o75pet.SetIdShell(ProcesosBatchEnum.COPAGO_BEN_MENS.getShell());
			o75pet.SetDescripcion(ProcesosBatchEnum.COPAGO_BEN_MENS.getLabelDesc(new Locale("es")));
			o75pet.SetParam1("@@" + proceso.getProceso98() + "@@" + proceso.getIdProceso98() + "@@");
			Map<String, String> resultado = this.lanzarPeticion_A_O75(o75pet, request, proceso);

			if (resultado.containsKey("codError")) {
				throw new Ad74aException(resultado.get("mensaje"));
			}

			return resultado.get("mensaje");

		} catch (Exception e) {
			throw new Ad74aException(e.getMessage());
		}

	}

	/**
	 * Updates a single row in the Procesos table.
	 *
	 * @param procesos Procesos
	 *
	 * @return Procesos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Procesos actualizarAError(Procesos procesos) {
		return this.procesosDao.actualizarAError(procesos);
	}
}
