<?php

namespace Drupal\dkan_dataset_archiver\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\File\FileSystem;
use Drupal\metastore_search\Search;
use Symfony\Component\DependencyInjection\ContainerInterface;
use ZipMerge\Zip\Stream\ZipMerge;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;

The following routes were removed from routing.yml and would need to be restored.


dkan_dataset_archiver.file_zip:
    class: Drupal\dkan_dataset_archiver\Controller\FileZipController
    arguments:
      - '@file_system'
      - '@request_stack'
      - '@dkan.metastore_search.service'

dkan_dataset_archiver.datasetSize:
  path: '/api/1/archive/dataset-size'
  methods: [GET]
  defaults:
    { _controller: '\Drupal\dkan_dataset_archiver\Controller\FileZipController::datasetSize' }
  requirements:
    _permission: 'access content'

dkan_dataset_archiver.datasetZipper:
  path: '/api/1/archive/dataset-zipper'
  methods: [GET]
  defaults:
    { _controller: '\Drupal\dkan_dataset_archiver\Controller\FileZipController::datasetZipper' }
  requirements:
    _permission: 'access content'

/**
 * Controller for using the ZipMerge functionality.
 *
 * This entire class is likely deprecated and not used. Keeping it only to
 * more fully decide on its deprecation later.
 */
class FileZipController extends ControllerBase implements ContainerInjectionInterface {

  /**
   * File system.
   *
   * @var \Drupal\Core\File\FileSystem
   */
  private FileSystem $fileSystem;

  /**
   * Request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  private RequestStack $requestStack;

  /**
   * Metastore search service.
   *
   * @var \Drupal\metastore_search\Search
   */
  private Search $searchService;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('file_system'),
      $container->get('request_stack'),
      $container->get('dkan.metastore_search.service')
    );
  }

  /**
   * {@inheritDoc}
   *
   * @param Drupal\Core\File\FileSystem $fileSystem
   *   Drupal FileSystem service.
   * @param Symfony\Component\HttpFoundation\RequestStack $requestStack
   *   RequestStack service for getting the current request.
   * @param Drupal\metastore_search\Search $metastoreSearchService
   *   Metastore Search wrapper for the SearchApi.
   */
  public function __construct(FileSystem $fileSystem, RequestStack $requestStack, Search $metastoreSearchService) {
    $this->fileSystem = $fileSystem;
    $this->requestStack = $requestStack;
    $this->searchService = $metastoreSearchService;
  }

  /**
   * Clean urls to make sure files exist in the resource folder.
   *
   * @param array $urls
   *   A comma separated string of urls.
   */
  private function cleanUrls(array $urls) {
    $filesFolder = $this->fileSystem->realpath('public://') . '/resources';
    $cleanUrls = [];
    foreach ($urls as $url) {
      // Prevent any potential dot operators from being used.
      if (str_contains($url, './')) {
        continue;
      }
      $urlArray = explode('/', $url);
      $lastTwo = array_slice($urlArray, -2);
      $resourceUrl = implode('/', $lastTwo);
      $filePath = $filesFolder . '/' . $resourceUrl . '.zip';
      if (file_exists($filePath)) {
        $cleanUrls[] = $filePath;
      }
    }
    return array_unique($cleanUrls);
  }

  /**
   * Get urls by keyword.
   *
   * @return array
   *   An array of urls.
   */
  protected function getUrls() {
    // Get the urls.
    $request = $this->requestStack->getCurrentRequest();

    $fulltext = NULL;
    if ($encoded_fulltext = $request->get('fulltext')) {
      $fulltext = urldecode($encoded_fulltext);
    }

    $theme = NULL;
    if ($encoded_theme = $request->get('theme')) {
      $theme = urldecode($encoded_theme);
    }

    $searchProperties = [
      'page-size' => 200,
    ];
    // For now, only support if both fulltext and theme are set.
    if ($fulltext && $theme) {
      $searchProperties['fulltext'] = $fulltext;
      $searchProperties['theme'] = $theme;
    }
    else {
      return [];
    }

    $responseBody = $this->searchService->search($searchProperties);

    $urls = [];
    foreach ($responseBody->results as $item) {
      $urls[] = $item->distribution[0]->downloadURL;
    }
    return $this->cleanUrls($urls);
  }

  /**
   * An api function that gets the size of the download of a set of zip files.
   *
   * @return Symfony\Component\HttpFoundation\Response
   *   A response object with the size of a group of files in bytes.
   *
   * @codeCoverageIgnore
   */
  public function datasetSize() {

    $urls = $this->getUrls();
    $fileSize = 0;

    // If the files exist in the resources folder, then get their file size.
    // $urls will be a comma separated string of urls, each in this format:
    // identifier_version/filename.csv.
    foreach ($urls as $url) {
      if (file_exists($url)) {
        $result = filesize($url);
        if ($result) {
          $fileSize += $result;
        }
      }
    }
    return new Response($fileSize);
  }

  /**
   * Serves up a zip file using zipMerge.
   *
   * @param bool $skipSend
   *   A flag for skipping sending the zip file, mainly for testing purposes.
   *
   * @return array
   *   An array of the final filename and the files that would download to
   *   verify values for testing.
   */
  public function datasetZipper($skipSend = FALSE) {
    $request = $this->requestStack->getCurrentRequest();
    $finalFilename = 'filtered-search-results.zip';
    $urls = NULL;

    // Only allow letters, numbers, dashes and underscores.
    $filename = $request->query->get('filename');
    if (!$filename) {
      $filename = '';
    }
    $filename = preg_replace("/[^a-zA-Z0-9-_]/", "", $filename);
    if ($filename) {
      $finalFilename = $filename . '-' . $finalFilename;
    }

    $urls = $this->getUrls();

    if (!$skipSend) {
      // Send a zip even if its blank to prevent frontend error.
      $zipMerge = new ZipMerge($finalFilename);
      if ($urls && count($urls)) {
        foreach ($urls as $url) {
          $zipMerge->appendZip($url);
        }
      }
      $zipMerge->finalize();
      exit;
    }
    return [$finalFilename, $urls];

  }

}
