<?php

declare(strict_types=1);

namespace KupShop\PreordersBundle\View;

use KupShop\CatalogBundle\ProductList\Builder\FilteredProductListInterface;
use KupShop\CatalogBundle\ProductList\FilterParams;
use KupShop\CatalogBundle\Util\FilteredProductList;
use KupShop\ComponentsBundle\View\ComponentsViewInterface;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Views\Traits\RequestTrait;
use KupShop\KupShopBundle\Views\View;
use KupShop\PreordersBundle\Dto\DateDateInfo;
use KupShop\PreordersBundle\Dto\PreorderDateInfo;
use KupShop\PreordersBundle\Entity\Preorder;
use KupShop\PreordersBundle\Entity\UserPreorder;
use KupShop\PreordersBundle\Service\DynamicContexts;
use KupShop\PreordersBundle\Service\PreorderProductsFetcher;
use KupShop\PreordersBundle\Service\PreorderUtil;
use KupShop\PreordersBundle\Translations\PreordersTranslation;
use KupShop\PreordersBundle\Twig\ComponentsTemplateOverride;
use KupShop\PreordersBundle\Util\UserPreordersDataUtil;
use KupShop\RestrictionsBundle\Utils\Restrictions;
use Query\Operator as Op;
use Query\Translation;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class PreordersView extends View implements ComponentsViewInterface
{
    use RequestTrait;
    use ComponentsTemplateOverride;

    protected $template = 'preOrdersItems.tpl';
    private ?int $dateId = null;
    private ?array $validDates = null;
    private ?array $validPreorders = null;

    private readonly bool $useMultipleDates;

    public function __construct(
        private readonly UserContext $userContext,
        private readonly FilteredProductList $listService,
        private readonly UrlGeneratorInterface $urlGenerator,
        private readonly PreorderProductsFetcher $productsFetcher,
        private readonly DynamicContexts $contexts,
        private readonly PreorderUtil $preorderUtil,
        private readonly ?Restrictions $restrictions,
    ) {
        $this->useMultipleDates = PreorderUtil::useMultipleDates();
    }

    public function getTitle(): string
    {
        return translate('preorders', 'preorders');
    }

    public function getResponse(?Request $request = null): Response
    {
        $dateId = $this->request->get('id_date');
        if ($dateId !== null) {
            $this->dateId = (int) $dateId;
        }

        if ($this->useMultipleDates) {
            $validIds = array_column($this->getValidPreorders(), 'id');
        } else {
            $validIds = array_column($this->getValidDates(), 'id');
        }

        $validIdSelected = $this->dateId === null || !in_array($this->dateId, $validIds);

        if (!$validIdSelected) {
            return parent::getResponse($request);
        }

        if (!empty($validIds)) {
            return new RedirectResponse(
                $this->urlGenerator->generate(
                    'kupshop_preorders_show_active', [
                        'id_date' => $validIds[0],
                    ]),
            );
        }

        $this->template = 'preOrdersDatesMissing.tpl';

        return parent::getResponse($request);
    }

    public function getBodyVariables(): array
    {
        $vars = parent::getBodyVariables();
        $vars['user'] = $this->userContext->getActive();
        $vars['custom_filter'] = $this->request->get('filter', []);
        $vars['listShow'] = $this->getListShowType();

        $vars += $this->getPreorderData();

        if (findModule(\Modules::USER_ADDRESSES)) {
            $vars['selectedAddress'] = !empty($vars['userData']['custom_data']['user_address']) ? (int) $vars['userData']['custom_data']['user_address'] : null;
        }

        return $vars;
    }

    private function getPreorderData(): array
    {
        $user = $this->userContext->getActive();
        $validDates = $this->getValidDates();

        if (!$user || empty($validDates)) {
            return [];
        }

        $userData = null;
        if ($this->useMultipleDates) {
            $preorder = new Preorder($this->dateId);
            $dateInfo = $this->createDateInfo($preorder, $filterRes);
        } else {
            $userPreorder = new UserPreorder((int) $user->id, (int) $this->dateId);
            $userData = UserPreordersDataUtil::get($userPreorder);
            $preorder = $userPreorder->getPreorder();
            $dateInfo = $this->createDateInfo($userPreorder, $filterRes);
        }

        // ! side effect - activates contexts globally
        $this->contexts->activatePreorder($preorder, $user);

        return [
            'date' => $dateInfo,
            'isDate' => !$this->useMultipleDates,
            'pager' => $filterRes->getPager(),
            'dynamic_filter' => [
                'filter' => $filterRes->getDynamicFilter(),
                'filterParams' => $filterRes->getFilterParams(),
            ],
            'userData' => $userData,
        ];
    }

    /**
     * @param FilteredProductListInterface|null $filterRes *OUT* param
     */
    private function createDateInfo(Preorder|UserPreorder $for, ?FilteredProductListInterface &$filterRes = null): DateDateInfo|PreorderDateInfo
    {
        $forSingleDate = $for instanceof UserPreorder;

        if ($forSingleDate) {
            $userPreorder = $for;
            $filterRes = $this->getProductList($userPreorder->getPreorder());
            $productsAndPrices = $this->productsFetcher->forSingleDate($filterRes->getProductList(), $userPreorder);

            return new DateDateInfo($userPreorder, $productsAndPrices, $this->preorderUtil);
        }

        // for a single preorder - multiple dates on a single page
        $preorder = $for;
        $user = $this->userContext->getActive();
        $filterRes = $this->getProductList($preorder);
        $productsAndPrices = $this->productsFetcher->forMultipleDates($filterRes->getProductList(), $preorder, $user);

        return new PreorderDateInfo($preorder, $user, $productsAndPrices);
    }

    private function getProductList(Preorder $preorder): FilteredProductListInterface
    {
        $dbcfg = \Settings::getDefault();
        $settings = $preorder->getSettings();

        $filterParams = new FilterParams();
        $filterParams->setInStore(null);

        if (($dbcfg->preorders['show_hidden'] ?? 'N') === 'Y') {
            $filterParams->setVisible(null);
        }

        $customFilter = $this->request->get('filter', []);

        if (!empty($customFilter['only_preordered'])) {
            $items = $preorder->getItemsByUser((int) $this->userContext->getActiveId());
            if (!$this->useMultipleDates) {
                $items = array_filter($items, fn (array $i) => (int) $i['id_preorder_date'] === $this->dateId);
            }

            $filterParams->andSpec(
                Op::inIntArray(array_column($items, 'id_product'), 'p.id'),
            );
        }

        if (findModule(\Modules::RESTRICTIONS)) {
            $filterParams->andSpec($this->restrictions->getRestrictionSpec());
        }

        /* @deprecated use \FilterParams::setSearch */
        if (!empty($customFilter['search'])) {
            $search = (string) $customFilter['search'];

            $filterParams->andSpec(Op::like([
                'p.title' => "%{$search}%",
                'p.code' => "%{$search}%",
            ], 'OR'));
        }

        $productsFilter = $settings['products_filter'];

        $this->listService->applyProductsFilter($filterParams, $productsFilter);

        $filterRes = $this->listService->createFromRequest($this->request, $filterParams);

        $orderByProductsFilterProductIds = !empty($productsFilter['products'])
            && !$this->request->get('order_by', $this->request->get('orderby'));

        if ($orderByProductsFilterProductIds) {
            $filterParams = new FilterParams();
            $this->listService->applyProductsFilterOrder($filterParams, $productsFilter);
            $filterRes->getProductList()->andSpec($filterParams->getSpec());
        }

        return $filterRes;
    }

    /**
     * @param int|null $preorderId if not supplied -> expects the `id_date` URL param to be a valid preorder date ID;
     *                             otherwise fetches current user's valid dates for the preorder which ID was supplied
     *
     * @return array<int, array> valid dates - **NOT** indexed by their ids
     */
    private function getValidDates(?int $preorderId = null): array
    {
        if ($preorderId === null && $this->validDates !== null) {
            return $this->validDates;
        }

        $user = $this->userContext->getActive();
        if (empty($user)) {
            return $this->validDates = [];
        }

        $dates = $this->preorderUtil->getOpenDates($user, $preorderId === null ? null : [$preorderId]);

        if ($preorderId === null) {
            $this->validDates = $dates;
        }

        return $dates;
    }

    /**
     * @return array<int, array> all preorders that have open preorder dates for current user;
     *                           array key is the preorder ID
     */
    private function getValidPreorders(): array
    {
        if ($this->validPreorders !== null) {
            return $this->validPreorders;
        }

        // get current user's valid and open preorder dates -> extract preorder ids
        $validPreorderIds = array_unique(array_column($this->getValidDates(), 'id_preorder'));

        $qb = sqlQueryBuilder()->select('po.*')
            ->from('preorders', 'po')
            ->andWhere(Op::inIntArray($validPreorderIds, 'po.id'))
            ->andWhere(Translation::coalesceTranslatedFields(PreordersTranslation::class));

        $validPreorders = $qb->execute()->fetchAllAssociative();
        $indexedByPreorderId = [];
        foreach ($validPreorders as $preorder) {
            $indexedByPreorderId[(int) $preorder['id']] = $preorder;
        }

        return $indexedByPreorderId;
    }

    // TMP: Nemělo by být potřeba
    public function getCorrectUrl(): ?string
    {
        return null;
    }

    private function getListShowType()
    {
        $show = \Settings::getDefault()->cat_show_style;

        if (!empty($_COOKIE['cat_show'])) {
            $show = $_COOKIE['cat_show'];
        }

        if (!empty($_GET['show']) && preg_match('/^[[:digit:]]{1}$/i', $_GET['show'])) {
            $show = $_GET['show'];
            SetCookies('cat_show', $show);
        }

        return $show;
    }

    public function getBreadcrumbs(): array
    {
        return getReturnNavigation(-1, 'USER', [
            [
                'text' => $this->getTitle(),
                'link' => path('kupshop_preorders_show_user'),
            ],
            [
                'text' => translate('preorder', 'preordersItems'),
            ],
        ]);
    }

    public function getWpjToolbar(): array
    {
        if ($this->useMultipleDates) {
            $url = getAdminUrl('preorders', ['ID' => $this->dateId]);
        } else {
            $url = getAdminUrl('preordersDates', ['ID' => $this->dateId]);
        }

        return array_merge(parent::getWpjToolbar(), [
            'url' => $url,
            'title' => translate(
                $this->useMultipleDates ? 'editPreorder' : 'editPreorderDate',
                'preordersFe',
            ),
        ]);
    }
}
