<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  System.Debug
 *
 * @copyright   (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\Plugin\System\Debug\Storage;

use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Filesystem\File;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Stores collected data into files
 *
 * @since  4.0.0
 */
class FileStorage extends \DebugBar\Storage\FileStorage
{
    /**
     * Saves collected data
     *
     * @param   string  $id    The log id
     * @param   string  $data  The log data
     *
     * @return  void
     *
     * @since  4.0.0
     */
    public function save($id, $data)
    {
        if (!file_exists($this->dirname)) {
            Folder::create($this->dirname);
        }

        $dataStr = '<?php die(); ?>#(^-^)#' . json_encode($data);

        File::write($this->makeFilename($id), $dataStr);
    }

    /**
     * Returns collected data with the specified id
     *
     * @param   string  $id  The log id
     *
     * @return  array
     *
     * @since  4.0.0
     */
    public function get($id)
    {
        $dataStr = file_get_contents($this->makeFilename($id));
        $dataStr = str_replace('<?php die(); ?>#(^-^)#', '', $dataStr);

        return json_decode($dataStr, true) ?: [];
    }

    /**
     * Returns a metadata about collected data
     *
     * @param   array    $filters  Filtering options
     * @param   integer  $max      The limit, items per page
     * @param   integer  $offset   The offset
     *
     * @return  array
     *
     * @since 4.0.0
     */
    public function find(array $filters = [], $max = 20, $offset = 0)
    {
        // Loop through all .php files and remember the modified time and id.
        $files = [];

        foreach (new \DirectoryIterator($this->dirname) as $file) {
            if ($file->getExtension() == 'php') {
                $files[] = [
                    'time' => $file->getMTime(),
                    'id'   => $file->getBasename('.php'),
                ];
            }
        }

        // Sort the files, newest first
        usort(
            $files,
            function ($a, $b) {
                if ($a['time'] === $b['time']) {
                    return 0;
                }

                return $a['time'] < $b['time'] ? 1 : -1;
            }
        );

        // Load the metadata and filter the results.
        $results = [];
        $i       = 0;

        foreach ($files as $file) {
            // When filter is empty, skip loading the offset
            if ($i++ < $offset && empty($filters)) {
                $results[] = null;
                continue;
            }

            $data = $this->get($file['id']);

            if (!$this->isSecureToReturnData($data)) {
                continue;
            }

            $meta = $data['__meta'];
            unset($data);

            if ($this->filter($meta, $filters)) {
                $results[] = $meta;
            }

            if (\count($results) >= ($max + $offset)) {
                break;
            }
        }

        return \array_slice($results, $offset, $max);
    }

    /**
     * Get a full path to the file
     *
     * @param   string  $id  The log id
     *
     * @return string
     *
     * @since 4.0.0
     */
    public function makeFilename($id)
    {
        return $this->dirname . basename($id) . '.php';
    }

    /**
     * Check if the user is allowed to view the request. Users can only see their own requests.
     *
     * @param   array  $data  The data item to process
     *
     * @return boolean
     *
     * @since 4.2.4
     */
    private function isSecureToReturnData($data): bool
    {
        /**
         * We only started this collector in Joomla 4.2.4 - any older files we have to assume are insecure.
         */
        if (!array_key_exists('juser', $data)) {
            return false;
        }

        $currentUser           = Factory::getUser();
        $currentUserId         = $currentUser->id;
        $currentUserSuperAdmin = $currentUser->authorise('core.admin');

        /**
         * Guests aren't allowed to look at other requests because there's no guarantee it's the same guest. Potentially
         * in the future this could be refined to check the session ID to show some requests. But it's unlikely we want
         * guests to be using the debug bar anyhow
         */
        if ($currentUserId === 0) {
            return false;
        }

        /** @var \Joomla\CMS\User\User $user */
        $user = Factory::getContainer()->get(UserFactoryInterface::class)
            ->loadUserById($data['juser']['user_id']);

        // Super users are allowed to look at other users requests. Otherwise users can only see their own requests.
        if ($currentUserSuperAdmin || $user->id === $currentUserId) {
            return true;
        }

        return false;
    }
}
