<?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 as Request;
use Psr\Http\Message\ResponseInterface as Response;
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 DownloadFileAction extends AbstractAction
{
    /**
     * Contains anis-server data path
     *
     * @var string
     */
    private $dataPath;

    /**
     * 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 array                  $settings Settings about token
     */
    public function __construct(EntityManagerInterface $em, string $dataPath, array $settings)
    {
        parent::__construct($em);
        $this->dataPath = $dataPath;
        $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(Request $request, Response $response, array $args): Response
    {
        if ($request->getMethod() === OPTIONS) {
            return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
        }

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

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

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

        // Search the file
        $filePath = $this->dataPath . $dataset->getFullDataPath() . DIRECTORY_SEPARATOR . $args['fpath'];

        // If the file not found 404
        if (!file_exists($filePath)) {
            throw new HttpNotFoundException(
                $request,
                'File with path ' . $args['fpath'] . ' is not found for the dataset ' . $datasetName
            );
        }

        // 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));
    }
}