package org.micord.service;

import java.io.FileNotFoundException;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import jakarta.annotation.PostConstruct;
import org.micord.config.ArangoDBConnection;
import org.micord.config.DatabaseConnection;
import org.micord.config.S3HttpConnection;
import org.micord.enums.RequestArgumentType;
import org.micord.models.requests.AqlRequest;
import org.micord.models.requests.AqlRequestParameter;
import org.micord.models.requests.BaseRequest;
import org.micord.models.requests.RequestArgument;
import org.micord.models.requests.RequestParameters;
import org.micord.models.requests.Requests;
import org.micord.models.requests.S3Request;
import org.micord.models.requests.SqlRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import com.arangodb.ArangoCursor;
import com.arangodb.ArangoDBException;
import com.arangodb.ArangoDatabase;
import com.arangodb.entity.StreamTransactionEntity;
import com.arangodb.model.AqlQueryOptions;
import com.arangodb.model.StreamTransactionOptions;

@Service
public class RequestService {

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

  private DocumentBuilderFactory secureDocumentFactory;
  @Autowired
  private HttpClient httpClient;

  @Autowired
  private ValidationService validationService;

  @PostConstruct
  public void init() {
    secureDocumentFactory = createSecureDocumentBuilderFactory();
  }

  private void processS3Request(S3Request request, RequestParameters parameters, Map<String, Boolean> validationResults) {
    logger.info("B. Starting processing of single S3 request");
    try {
      List<String> files = new ArrayList<>();
      List<String> ids = parameters.getIds();

      if (request.getRequestArguments() != null && !request.getRequestArguments().isEmpty()) {
        for (RequestArgument argument : request.getRequestArguments()) {
          try (Connection connection = DatabaseConnection.getConnection(
              argument.getRequestArgumentConnectionParams())) {

            Map<String, Object> query = buildSqlQueryForS3(argument.getRequestArgumentURL(), parameters, validationResults);
            logger.info("C. Calling query {} for ids {}: ", query.get("requestURL"), ids);
            logger.debug("Starting fetching paths from database for S3 request");
            long startExecTime = System.currentTimeMillis();

            List<String> result = fetchFileListFromDatabaseSQL(connection, (String) query.get("requestURL"));
            String formattedResult = IntStream.range(0, result.size())
                .mapToObj(i -> (i + 1) + ". " + result.get(i))
                .collect(Collectors.joining("\n"));

            logger.info("D. Found files for query {}:\n{}", query.get("requestURL"), formattedResult);

            if (result != null && !result.isEmpty()) {
              files.addAll(result);
            }
            long endExecTime = System.currentTimeMillis();
            logger.debug("Paths fetched in {} ms", endExecTime - startExecTime);
          } catch (SQLException e) {
            logger.error("Failed to execute query for RequestArgument: {}", argument.getRequestArgumentURL(), e);
            throw new RuntimeException("Database query error for argument: " + argument.getRequestArgumentURL(), e);
          }
        }
      }

      String formattedFiles = IntStream.range(0, files.size())
          .mapToObj(i -> (i + 1) + ". " + files.get(i))
          .collect(Collectors.joining("\n"));

      logger.info("E. Found files for ids {}:\n{}", ids, formattedFiles);

      if (files.isEmpty()) {
        logger.warn("No files found for S3 request {}", request);
      } else {

        for (String file : files) {
          logger.info("F. Starting query S3 for file: {}", file);
          try {
            processFileForS3Request(request, file);
          } catch (RuntimeException e) {
            logger.error("Error processing file: {}", file, e);
            throw e; // Rethrow to propagate for exception handling
          }
        }

      }

    } catch (Exception e) {
      logger.error("Failed to process S3 request: {}", request, e);
      throw e; // Rethrow exception to propagate to the handler
    }
  }

  private void processS3Request(S3Request request, List<String> ids, Map<String, Boolean> validationResults) {
    logger.info("B. Starting processing of single S3 request");
    Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false);
    try {
      List<String> files = new ArrayList<>();

      if (request.getRequestArguments() != null && !request.getRequestArguments().isEmpty()) {
        for (RequestArgument argument : request.getRequestArguments()) {
          try (Connection connection = DatabaseConnection.getConnection(
              argument.getRequestArgumentConnectionParams())) {

            String requestURL = argument.getRequestArgumentURL();
            if (!emptyIdsAllowed) {
              Map<String, Object> sqlQueryForS3 = buildSqlQueryForS3(argument.getRequestArgumentURL(), ids);
              requestURL = (String) sqlQueryForS3.get("requestURL");
            }
            logger.info("C. Calling query {} for ids {}: ", requestURL, ids);
            logger.debug("Starting fetching paths from database for S3 request");
            long startExecTime = System.currentTimeMillis();

            List<String> result = fetchFileListFromDatabaseSQL(connection, requestURL);
            String formattedResult = IntStream.range(0, result.size())
                .mapToObj(i -> (i + 1) + ". " + result.get(i))
                .collect(Collectors.joining("\n"));

            logger.info("D. Found files for query {}:\n{}", requestURL, formattedResult);

            if (result != null && !result.isEmpty()) {
              files.addAll(result);
            }
            long endExecTime = System.currentTimeMillis();
            logger.debug("Paths fetched in {} ms", endExecTime - startExecTime);
          } catch (SQLException e) {
            logger.error("Failed to execute query for RequestArgument: {}", argument.getRequestArgumentURL(), e);
            throw new RuntimeException("Database query error for argument: " + argument.getRequestArgumentURL(), e);
          }
        }
      }

      String formattedFiles = IntStream.range(0, files.size())
          .mapToObj(i -> (i + 1) + ". " + files.get(i))
          .collect(Collectors.joining("\n"));

      logger.info("E. Found files for ids {}:\n{}", ids, formattedFiles);

      if (files.isEmpty()) {
        logger.warn("No files found for S3 request {}", request);
      } else {

        for (String file : files) {
          logger.info("F. Starting query S3 for file: {}", file);
          try {
            processFileForS3Request(request, file);
          } catch (RuntimeException e) {
            logger.error("Error processing file: {}", file, e);
            throw e; // Rethrow to propagate for exception handling
          }
        }

      }

    } catch (Exception e) {
      logger.error("Failed to process S3 request: {}", request, e);
      throw e; // Rethrow exception to propagate to the handler
    }
  }

  private void processFileForS3Request(S3Request request, String file) {
    if (file == null || file.isBlank()) {
      logger.warn("Skipping invalid file path: {}", file);
      throw new RuntimeException("Invalid file path");
    }

    try {
      logger.debug("Starting building HTTP request for file: {}", file);
      long startExecTime = System.currentTimeMillis();
      HttpRequest httpRequest = S3HttpConnection.buildHttpRequest(request, file);
      long endExecTime = System.currentTimeMillis();
      logger.debug("HTTP request built in {} ms for file: {}", endExecTime - startExecTime, file);

      HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
      if (response.statusCode() == HttpURLConnection.HTTP_NO_CONTENT || response.statusCode() == HttpURLConnection.HTTP_OK) {
        logger.info("Successfully deleted object {}", file);
      } else {
        handleErrorResponse(response, file);
      }
    } catch (Exception e) {
      logger.error("Error sending HTTP request for file: {}", file, e);
      throw new RuntimeException("HTTP request error for file: " + file, e);
    }
  }

  private void handleErrorResponse(HttpResponse<String> response, String file) {
    try {
      DocumentBuilder builder = secureDocumentFactory.newDocumentBuilder();
      InputSource is = new InputSource(new StringReader(response.body()));
      Document doc = builder.parse(is);
      Element root = doc.getDocumentElement();
      String message = root.getElementsByTagName("Message").item(0).getTextContent();
      logger.error("Failed to delete object {}. Response code: {}. Error message: {}", file, response.statusCode(), message);
      throw new RuntimeException("Failed to delete object: " + file);
    } catch (Exception e) {
      logger.error("Failed to parse error response for file {}", file, e);
      throw new RuntimeException("Error parsing HTTP response: " + response.body(), e);
    }
  }


  @Transactional
  public void processSqlAndAqlRequests(Requests config, List<String> ids) throws SQLException, FileNotFoundException {
    logger.debug("Starting transactional processing of requests");
    if (config.getS3Requests() != null && !config.getS3Requests().isEmpty()) {
      logger.info("A. Starting processing of S3 requests");

      for (S3Request request : config.getS3Requests()) {
        Map<String, Boolean> validationResults = validationService.validateRequest(request, ids);
        processS3Request(request, ids, validationResults);
      }
    }

    if (config.getSqlRequests() != null) {
      for (SqlRequest request : config.getSqlRequests()) {
        Map<String, Boolean> validationResults = validationService.validateRequest(request, ids);
        processSqlRequests(request, ids, validationResults);
      }
    }

    if (config.getAqlRequests() != null) {
      for (AqlRequest request : config.getAqlRequests()) {
        Map<String, Boolean> validationResults = validationService.validateRequest(request, ids);
        processAqlRequests(request, ids, validationResults);
      }
    }
  }

  @Transactional
  public void processSqlAndAqlRequests(Requests config, RequestParameters parameters) throws SQLException, FileNotFoundException {
    logger.debug("Starting transactional processing of requests");
	  if (config.getS3Requests() != null && !config.getS3Requests().isEmpty()) {

      logger.info("A. Starting processing of S3 requests");

      for (S3Request request : config.getS3Requests()) {
        Map<String, Boolean> validationResults = validationService.validateMilitaryNoticeRequest(request, parameters);
        processS3Request(request, parameters, validationResults);
      }
    }

    if (config.getSqlRequests() != null) {
      for (SqlRequest request : config.getSqlRequests()) {
        Map<String, Boolean> validationResults = validationService.validateMilitaryNoticeRequest(request, parameters);
        processSqlRequests(request, parameters, validationResults);
      }
    }

    if (config.getAqlRequests() != null) {
      for (AqlRequest request : config.getAqlRequests()) {
        Map<String, Boolean> validationResults = validationService.validateMilitaryNoticeRequest(request, parameters);
        processAqlRequests(request, parameters.getIds(), validationResults);
      }
    }
  }

  private void processSqlRequests(SqlRequest request, List<String> ids, Map<String, Boolean> validationResults) {
    logger.debug("Starting transactional processing of SQL requests");
    Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false);
    String requestURL;
    Map<String, Object> query = null;

    if (emptyIdsAllowed != null && emptyIdsAllowed) {
      requestURL = request.getRequestURL();
      logger.info("Empty IDs allowed. Using original request URL: {}", requestURL);
    } else {
      query = buildSqlQuery(request, ids);
      requestURL = (String) query.get("requestURL");
    }

    logger.debug("Opening connection for SQL Request: {}", request.getRequestURL().replaceAll("password=\\S+", "password=***"));
    long startExecTime = System.currentTimeMillis();
    try (Connection connection = DatabaseConnection.getConnection(request.getSqlConnectionParams())) {
      executeSqlQuery(connection, requestURL);

      if (query != null) {
        List<String> queryIds = (List<String>) query.get("ids");
        if (queryIds != null && !queryIds.isEmpty()) {
          ids.addAll(queryIds);
        } else {
          logger.warn("No IDs found for the query");
        }
      }
      long endExecTime = System.currentTimeMillis();
      logger.debug("SQL request executed in {} ms", endExecTime - startExecTime);
      logger.info("Successfully executed query {} for IDs: ({})", requestURL, String.join(", ", ids));
    } catch (SQLException e) {
      logger.error("SQL execution failed for query: {}", requestURL, e);
      throw new RuntimeException("Error executing SQL query", e);
    }
  }

  private void processSqlRequests(SqlRequest request, RequestParameters parameters, Map<String, Boolean> validationResults) {
    logger.debug("Starting transactional processing of SQL requests");
    Map<String, Object> query = buildSqlQuery(request, parameters, validationResults);
    List<String> ids = parameters.getIds();
    logger.debug("Opening connection for SQL Request: {}", request.getRequestURL().replaceAll("password=\\S+", "password=***"));
    long startExecTime = System.currentTimeMillis();
    try (Connection connection = DatabaseConnection.getConnection(
        request.getSqlConnectionParams())) {
      String requestURL = (String) query.get("requestURL");
      executeSqlQuery(connection, requestURL);

      List<String> queryIds = (List<String>) query.get("ids");
      if (queryIds != null && !queryIds.isEmpty()) {
        ids.addAll(queryIds);
      } else {
        logger.warn("No IDs found for the query");
      }
      long endExecTime = System.currentTimeMillis();
      logger.debug("SQL request executed in {} ms", endExecTime - startExecTime);

      logger.info("Successfully executed query {} for IDs: ({})", requestURL, String.join(", ", ids));
    }
    catch (SQLException e) {
      logger.error("SQL execution failed for query: {}", query, e);
      throw new RuntimeException("Error executing SQL query", e);
    }
  }

  private Map<String, Object> buildSqlQueryForS3(String requestURL, List<String> ids) {
    logger.debug("Starting building SQL query for request: {}", requestURL);
    long startExecTime = System.currentTimeMillis();
    Map<String, Object> resultMap = new HashMap<>();
    String endpointArguments;

    if (requestURL.contains(":=")) {
      endpointArguments = "'{" + ids.stream()
          .map(String::trim)
          .collect(Collectors.joining(", ")) + "}'";
    } else {
      endpointArguments = "(" + ids.stream()
          .map(s -> "'" + s.trim() + "'")
          .collect(Collectors.joining(", ")) + ")";
    }

    resultMap.put("requestURL", requestURL
        .replace("${endpointArguments}", endpointArguments));

    long endExecTime = System.currentTimeMillis();
    logger.debug("SQL query for S3 built in {} ms", endExecTime - startExecTime);

    return resultMap;
  }

  private Map<String, Object> buildSqlQueryForS3(String url, RequestParameters parameters, Map<String, Boolean> validationResults) {
    logger.debug("Starting building SQL query for request: {}", url);
    long startExecTime = System.currentTimeMillis();
    Map<String, Object> resultMap = new HashMap<>();

    Boolean isEmptyDatesAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_DATES_ALLOWED, false);

    String requestURL = applyDateFilter(url, parameters.getStartDate(), parameters.getEndDate(), isEmptyDatesAllowed);
    if (isEmptyDatesAllowed) {
      logger.info("Skipping date filtering as empty dates are allowed.");
    }

    String finalUrl = applyEndpointArguments(requestURL, parameters.getIds());
    resultMap.put("requestURL", finalUrl);

    long endExecTime = System.currentTimeMillis();
    logger.debug("SQL query for S3 built in {} ms", endExecTime - startExecTime);

    return Collections.unmodifiableMap(resultMap);
  }

  private String applyDateFilter(String url, LocalDate startDate, LocalDate endDate, boolean skipFiltering) {
    if (!skipFiltering) {
      return prepareDatesFilterInRequestURL(url, startDate, endDate);
    }
    return url;
  }

  private String applyEndpointArguments(String requestURL, List<String> ids) {
    String endpointArguments = formatEndpointArguments(requestURL, ids);
    return requestURL.replace("${endpointArguments}", endpointArguments);
  }

  private String formatEndpointArguments(String requestURL, List<String> ids) {
    if (requestURL.contains(":=")) {
      return "'{" + ids.stream()
          .map(String::trim)
          .collect(Collectors.joining(", ")) + "}'";
    } else {
      return "(" + ids.stream()
          .map(s -> "'" + s.trim() + "'")
          .collect(Collectors.joining(", ")) + ")";
    }
  }

  private String prepareDatesFilterInRequestURL(String requestURL, LocalDate startDate, LocalDate endDate) {

    if (startDate != null) {
      requestURL = requestURL.replace("${startDate}", startDate.toString());
    }

    if (endDate != null) {
      requestURL = requestURL.replace("${endDate}", endDate.toString());
    }

    return requestURL;
  }

  private Map<String, Object> buildSqlQuery(BaseRequest request, List<String> ids) {
    logger.debug("Starting building SQL query for request: {}", request.getRequestURL().replaceAll("password=\\S+", "password=***"));
    long startExecTime = System.currentTimeMillis();
    Map<String, Object> resultMap = new HashMap<>();
    String endpointArguments;

    String requestURL = request.getRequestURL();

    if (requestURL.contains(":=")) {
      endpointArguments = "'{" + ids.stream()
          .map(String::trim)
          .collect(Collectors.joining(", ")) + "}'";
    } else {
      endpointArguments = "(" + ids.stream()
          .map(s -> "'" + s.trim() + "'")
          .collect(Collectors.joining(", ")) + ")";
    }

    if (request.getRequestArguments() != null && !request.getRequestArguments().isEmpty()) {
      for (RequestArgument argument : request.getRequestArguments()) {

        if (argument.getRequestArgumentConnectionParams() != null) {
          logger.debug("Opening connection for SQL RequestArgument: {}", argument.getRequestArgumentName());
          try (Connection connection = DatabaseConnection.getConnection(
              argument.getRequestArgumentConnectionParams())) {
            String query = argument.getRequestArgumentURL();
            List<String> result = fetchFileListFromDatabaseSQL(connection, query);

            resultMap.put("ids", result);


            if (result != null && !result.isEmpty()) {
              String resultSet = "(" + result.stream()
                  .map(s -> "'" + s.trim() + "'")
                  .collect(Collectors.joining(", ")) + ")";

              requestURL = requestURL.replace("${" + argument.getRequestArgumentName() + "}", resultSet);

            }

          }
          catch (SQLException e) {
            logger.error("Failed to execute query for RequestArgument", e);
          }
        }
      }
    }

    resultMap.put("requestURL", requestURL
        .replace("${endpointArguments}", endpointArguments));

    long endExecTime = System.currentTimeMillis();
    logger.debug("SQL query built in {} ms", endExecTime - startExecTime);

    return resultMap;
  }

  private Map<String, Object> buildSqlQuery(BaseRequest request, RequestParameters parameters, Map<String, Boolean> validationResults) {
    logger.debug("Starting building SQL query for request: {}", request.getRequestURL().replaceAll("password=\\S+", "password=***"));
    long startExecTime = System.currentTimeMillis();
    Map<String, Object> resultMap = new HashMap<>();

    Boolean isEmptyDatesAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_DATES_ALLOWED, false);

    String requestURL = applyDateFilter(request.getRequestURL(), parameters.getStartDate(), parameters.getEndDate(), isEmptyDatesAllowed);
    if (isEmptyDatesAllowed) {
      logger.info("Skipping date filtering as empty dates are allowed.");
    }
    List<String> ids = parameters.getIds();

    if (request.getRequestArguments() != null && !request.getRequestArguments().isEmpty()) {
      for (RequestArgument argument : request.getRequestArguments()) {
        if (argument.getRequestArgumentConnectionParams() != null) {
          logger.debug("Opening connection for SQL RequestArgument: {}", argument.getRequestArgumentName());
          try (Connection connection = DatabaseConnection.getConnection(argument.getRequestArgumentConnectionParams())) {
            String query = argument.getRequestArgumentURL();
            List<String> result = fetchFileListFromDatabaseSQL(connection, query);
            resultMap.put("ids", result);

            if (result != null && !result.isEmpty()) {
              String resultSet = "(" + result.stream()
                  .map(s -> "'" + s.trim() + "'")
                  .collect(Collectors.joining(", ")) + ")";
              requestURL = requestURL.replace("${" + argument.getRequestArgumentName() + "}", resultSet);
            }
          } catch (SQLException e) {
            logger.error("Failed to execute query for RequestArgument", e);
          }
        }
      }
    }

    String finalUrl = applyEndpointArguments(requestURL, ids);
    resultMap.put("requestURL", finalUrl);

    long endExecTime = System.currentTimeMillis();
    logger.debug("SQL query built in {} ms", endExecTime - startExecTime);

    return resultMap;
  }


  private boolean executeSqlQuery(Connection connection, String query) throws SQLException {
    try (PreparedStatement stmt = connection.prepareStatement(query)) {
      return stmt.execute();
    }
  }

  public List<String> fetchFileListFromDatabaseSQL(Connection connection, String query)
      throws SQLException {
    List<String> results = new ArrayList<>();
    try (PreparedStatement stmt = connection.prepareStatement(query);
         ResultSet rs = stmt.executeQuery()) {
      while (rs.next()) {
        results.add(rs.getString(1)); // Fetch the first column
      }
    }
    return results;
  }

  private void processAqlRequests(AqlRequest request, List<String> ids, Map<String, Boolean> validationResults) {
    ArangoDatabase arangoDb = ArangoDBConnection.getConnection(request.getAqlConnectionParams());

    // TODO: implement for multiple request arguments
    RequestArgument requestArgument = request.getRequestArguments().get(0);

    List<String> collectionNames = new ArrayList<>();

    request.getAqlRequestCollections().forEach(collection -> {
        collectionNames.add(collection.getType());
    });
    StreamTransactionEntity tx = null;
    try {
      StreamTransactionOptions options = new StreamTransactionOptions()
          .writeCollections(collectionNames.toArray(new String[0]))
          .readCollections(collectionNames.toArray(new String[0]));

      tx = arangoDb.beginStreamTransaction(options);
      String transactionId = tx.getId();

      logger.info("Stream transaction started with ID: {}", transactionId);

      Map<String, Object> entities = executeSelectAqlRequest(arangoDb, request, requestArgument, ids, transactionId, validationResults);

      if (entities.isEmpty()) {
        logger.warn("No entities found for main AQL request.");
        return;
      }

      request.getAqlRequestCollections().forEach(collection -> {
        String type = collection.getType();
        String entityType;

        if (Objects.equals(type, "applications")) {
          entityType = "applicationId";
        } else {
          entityType = type + "Id";
        }

        Object entityIds = entities.get(entityType);

        if (entityIds instanceof String) {
          entityIds = Collections.singletonList((String) entityIds);
        }

        executeMainAqlRequest(arangoDb, entityIds, collection.getCollectionUrl(), type, transactionId);
      });

      arangoDb.commitStreamTransaction(transactionId);
      logger.info("Stream transaction with ID {} committed successfully", transactionId);

    }
    catch (ArangoDBException e) {
      if (tx != null) {
        arangoDb.abortStreamTransaction(tx.getId());
        logger.error("Stream transaction with ID {} aborted due to an error", tx.getId(), e);
      }
      throw new RuntimeException("Failed to execute AQL request within a stream transaction", e);
    }

    logger.info("Successfully executed AQL request");
  }

  private Map<String, Object> executeSelectAqlRequest(ArangoDatabase arangoDb,
                                                      AqlRequest request,
                                                      RequestArgument requestArgument,
                                                      List<String> ids, String transactionId, Map<String, Boolean> validationResults) {
    Map<String, Object> entities = new HashMap<>();

    String url = requestArgument.getRequestArgumentURL();
    RequestArgumentType type = requestArgument.getType();

    if (type == RequestArgumentType.AQL) {
      Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false);

      Map<String, Object> bindVars = new HashMap<>();

      if (!emptyIdsAllowed) bindVars.put("ids", ids);

      if (request.getAqlRequestParameters() != null) {
        for (AqlRequestParameter parameter : request.getAqlRequestParameters().getParameters()) {
          try (Connection connection = DatabaseConnection.getConnection(parameter.getSqlConnectionParams())) {
            String sqlQuery = parameter.getAqlRequestParameterURL();
            if (sqlQuery.contains("${endpointArguments}")) {
              String inClause = ids.stream()
                  .map(id -> "'" + id + "'")
                  .collect(Collectors.joining(", "));
              sqlQuery = sqlQuery.replace("${endpointArguments}", "(" + inClause + ")");
            }
            
            logger.info("Executing SQL query: {}", sqlQuery);
            List<String> parameterValues = fetchFileListFromDatabaseSQL(connection, sqlQuery);
            if (parameterValues != null && !parameterValues.isEmpty()) {
                logger.info("Parameter type: {}; values: {}", parameter.getType(), parameterValues.get(0));
            } else {
                logger.info("No values found for parameter type: {}", parameter.getType());
            }
            bindVars.put(parameter.getType(), parameterValues);
          } catch (SQLException e) {
            logger.error("Failed to fetch parameter values for type: {}", parameter.getType(), e);
            throw new RuntimeException("Failed to execute SQL query: " + e.getMessage(), e);
          }
        }
      }

      AqlQueryOptions aqlQueryOptions = new AqlQueryOptions().streamTransactionId(transactionId);

      bindVars.forEach((key, value) -> {
        logger.info("Key: {}; Value: {}", key, value);
      });

      try (ArangoCursor<Map> cursor = arangoDb.query(url, Map.class, bindVars, aqlQueryOptions)) {
        while (cursor.hasNext()) {
          Map<String, Object> result = cursor.next();

          for (Map.Entry<String, Object> entry : result.entrySet()) {
            String key = entry.getKey();
            Object entityValue = entry.getValue();

            entities.put(key, entityValue);
          }
        }
      }
      catch (Exception e) {
        logger.error("Failed to execute AQL url", e);
      }
    }

    return entities;
  }

  private void executeMainAqlRequest(ArangoDatabase arangoDb, Object entityIds, String requestURL, String type, String transactionId) {


    Map<String, Object> bindVars = new HashMap<>();
    bindVars.put("ids", entityIds);

    AqlQueryOptions aqlQueryOptions = new AqlQueryOptions().streamTransactionId(transactionId);

    try {
      arangoDb.query(requestURL, null, bindVars, aqlQueryOptions);
      logger.info("Successfully removed {} for ids: {}", type, entityIds);
    } catch (Exception e) {
      logger.error("Failed to execute AQL request: {}", e.getMessage(), e);
    }
  }

  private DocumentBuilderFactory createSecureDocumentBuilderFactory() {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    try {
      factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
    }
    catch (Exception e) {
      throw new RuntimeException("Failed to secure XML parser", e);
    }
    return factory;
  }
}
