<?php
declare(strict_types=1);

namespace Autogedal\BlackFriday\Controller\Products;

use Autogedal\BlackFriday\Model\Config;
use Autogedal\BlackFriday\Model\ProductProvider;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Controller\Result\JsonFactory;
use Psr\Log\LoggerInterface;

/** JSON list of the landing page: tabs, car filters, one page of products or favourites. Not cached (prices and stock). */
class Index implements HttpGetActionInterface
{
    private const MAX_FAVORITES = 300;

    public function __construct(
        private readonly RequestInterface $request,
        private readonly JsonFactory $jsonFactory,
        private readonly ProductProvider $productProvider,
        private readonly Config $config,
        private readonly LoggerInterface $logger
    ) {
    }

    public function execute()
    {
        $result = $this->jsonFactory->create();
        $result->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0', true);
        if (!$this->config->isEnabled()) {
            return $result->setHttpResponseCode(404)->setData(['error' => true]);
        }
        try {
            $favorites = $this->favorites();
            $forceLive = $this->forcedMode();
            if ($this->request->getParam('tab') === 'fav') {
                return $result->setData($this->productProvider->getFavorites($favorites ?? [], (int)$this->request->getParam('offset'), $forceLive));
            }
            $tabId = (int)$this->request->getParam('tab');
            // no category chosen: one row per category, then the mixed list ("load more": mix=1, offset, seed)
            if (!$tabId) {
                return $result->setData($this->productProvider->getOverview(
                    (bool)$this->request->getParam('mix'),
                    (int)$this->request->getParam('offset'),
                    (int)$this->request->getParam('seed'),
                    $favorites,
                    $forceLive
                ));
            }
            return $result->setData($this->productProvider->getList(
                $tabId,
                (int)$this->request->getParam('brand'),
                (int)$this->request->getParam('model'),
                (int)$this->request->getParam('p', 1),
                $favorites,
                $forceLive,
                $this->extraFilters()
            ));
        } catch (\Throwable $e) {
            $this->logger->error('Black Friday products: ' . $e->getMessage());
            return $result->setHttpResponseCode(500)->setData(['error' => true]);
        }
    }

    /** @return array<string, int> category filters (f[auto_body]=id ...); the provider keeps only the ones of the category */
    private function extraFilters(): array
    {
        $filters = $this->request->getParam('f');
        if (!is_array($filters)) {
            return [];
        }
        $result = [];
        foreach ($filters as $code => $value) {
            if (is_string($code) && is_scalar($value) && (int)$value > 0) {
                $result[$code] = (int)$value;
            }
        }
        return $result;
    }

    /** Mode chosen in the test panel (only while the test mode is on in the admin); null = the real one */
    private function forcedMode(): ?bool
    {
        if (!$this->config->isTestMode()) {
            return null;
        }
        $mode = (string)$this->request->getParam('mode');
        return $mode === 'live' ? true : ($mode === 'pre' ? false : null);
    }

    /** @return string[]|null favourite SKUs of the browser (comma separated, most recent first); null when not sent */
    private function favorites(): ?array
    {
        $raw = $this->request->getParam('fav');
        if (!is_string($raw)) {
            return null;
        }
        $skus = array_values(array_unique(array_filter(array_map('trim', explode(',', $raw)), 'strlen')));
        return array_slice($skus, 0, self::MAX_FAVORITES);
    }
}
