<?php

/*
 * This file is part of Anis Server.
 *
 * (c) Laboratoire d'Astrophysique de Marseille / CNRS
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
declare(strict_types=1);

namespace App\Action;

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Doctrine\ORM\EntityManagerInterface;
use Slim\Exception\HttpNotFoundException;
use Nyholm\Psr7\Factory\Psr17Factory;

/**
 * @author François Agneray <francois.agneray@lam.fr>
 * @package App\Action
 */
final class DownloadArchiveAction extends AbstractAction
{
    /**
     * Contains anis-server data path
     *
     * @var string
     */
    private $dataPath;

    /**
     * Contains archive folder path
     *
     * @var string
     */
    private $archiveFolder;

    /**
     * Contains settings to handle Json Web Token
     *
     * @var array
     */
    private $settings;

    /**
     * Create the classe before call __invoke to execute the action
     *
     * @param EntityManagerInterface $em            Doctrine Entity Manager Interface
     * @param string                 $dataPath      Contains anis-server data path
     * @param string                 $archiveFolder Contains archive folder path
     * @param array                  $settings      Settings about token
     */
    public function __construct(EntityManagerInterface $em, string $dataPath, string $archiveFolder, array $settings)
    {
        parent::__construct($em);
        $this->dataPath = $dataPath;
        $this->archiveFolder = $archiveFolder;
        $this->settings = $settings;
    }

    /**
     * `GET` Returns the file found
     *
     * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
     * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
     * @param  string[]               $args     This table contains information transmitted in the URL (see routes.php)
     *
     * @return ResponseInterface
     */
    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        array $args
    ): ResponseInterface {
        if ($request->getMethod() === OPTIONS) {
            return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
        }

        // Search the correct dataset with primary key
        $dataset = $this->em->find('App\Entity\Dataset', $args['dname']);

        // If dataset is not found 404
        if (is_null($dataset)) {
            throw new HttpNotFoundException(
                $request,
                'Dataset with name ' . $args['dname'] . ' is not found'
            );
        }

        // If instance is private and authorization enabled
        $instance = $dataset->getDatasetFamily()->getInstance();
        if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
            $this->verifyInstanceAuthorization(
                $request,
                $instance->getName(),
                explode(',', $this->settings['admin_roles'])
            );
        }

        // If dataset is private and authorization enabled
        if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
            $this->verifyDatasetAuthorization(
                $request,
                $dataset->getName(),
                explode(',', $this->settings['admin_roles'])
            );
        }

        // Search the file
        $archiveId = $args['id'];
        $filePath = $this->dataPath . $this->archiveFolder . '/' . $archiveId . '.zip';

        // If the file not found 404
        if (!file_exists($filePath)) {
            throw new HttpNotFoundException(
                $request,
                'Archive file with name ' . $archiveId . '.zip is not found'
            );
        }

        // If the file found so stream it
        $psr17Factory = new Psr17Factory();
        $stream = $psr17Factory->createStreamFromFile($filePath, 'r');

        return $response->withBody($stream)
            ->withHeader('Content-Disposition', 'attachment; filename=' . basename($filePath) . ';')
            ->withHeader('Content-Type', mime_content_type($filePath))
            ->withHeader('Content-Length', filesize($filePath));
    }
}