<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\PageBuilder\Rendering;

use CpCreation\VitiCore\PageBuilder\Model\Page;
use CpCreation\VitiCore\PageBuilder\Model\PageSection;
use CpCreation\VitiCore\PageBuilder\Repository\PageSectionRepository;

final readonly class PageLayoutResolver
{
    public const MODE_GLOBAL = 'global';
    public const MODE_CUSTOM = 'custom';
    public const MODE_NONE = 'none';
    public const GLOBAL_HEADER_PAGE_CODE = 'layout__header';
    public const GLOBAL_FOOTER_PAGE_CODE = 'layout__footer';

    public function __construct(
        private PageLayoutManager $pageLayoutManager,
        private PageSectionRepository $pageSectionRepository,
    ) {
    }

    public function resolve(?Page $page, bool $builderMode = false): PageLayout
    {
        if (!$page instanceof Page) {
            return new PageLayout(self::MODE_NONE, [], self::MODE_NONE, []);
        }

        $pageSections = $this->sectionsForPage($page, $builderMode);
        $headerMode = $this->normalizeMode($page->getSetting('headerMode', self::MODE_GLOBAL));
        $footerMode = $this->normalizeMode($page->getSetting('footerMode', self::MODE_GLOBAL));

        return new PageLayout(
            $headerMode,
            $this->resolveZone($page, $pageSections, 'header', self::GLOBAL_HEADER_PAGE_CODE, $headerMode, $builderMode),
            $footerMode,
            $this->resolveZone($page, $pageSections, 'footer', self::GLOBAL_FOOTER_PAGE_CODE, $footerMode, $builderMode),
        );
    }

    /**
     * @param list<PageSection> $pageSections
     *
     * @return list<PageSectionRender>
     */
    private function resolveZone(
        Page $page,
        array $pageSections,
        string $zone,
        string $globalPageCode,
        string $mode,
        bool $builderMode,
    ): array {
        return match ($mode) {
            self::MODE_NONE => [],
            self::MODE_CUSTOM => $this->componentRenders($this->filterByZone($pageSections, $zone)),
            default => $this->componentRenders($this->globalSections($page, $globalPageCode, $builderMode)),
        };
    }

    /**
     * @return list<PageSection>
     */
    private function sectionsForPage(Page $page, bool $builderMode): array
    {
        return $builderMode
            ? $this->pageSectionRepository->findByPageCodeAdmin($page->getCode())
            : $this->pageSectionRepository->findActiveForPageCode($page->getCode());
    }

    /**
     * @return list<PageSection>
     */
    private function globalSections(Page $currentPage, string $pageCode, bool $builderMode): array
    {
        if ($currentPage->getCode() === $pageCode) {
            return $this->filterByZone($this->sectionsForPage($currentPage, $builderMode), 'main');
        }

        $page = self::GLOBAL_HEADER_PAGE_CODE === $pageCode
            ? $this->pageLayoutManager->ensureGlobalHeaderPage()
            : $this->pageLayoutManager->ensureGlobalFooterPage();

        return $this->filterByZone($this->sectionsForPage($page, $builderMode), 'main');
    }

    /**
     * @param iterable<PageSection> $sections
     *
     * @return list<PageSection>
     */
    private function filterByZone(iterable $sections, string $zone): array
    {
        $filtered = [];
        foreach ($sections as $section) {
            if (!$section instanceof PageSection) {
                continue;
            }

            $sectionZone = trim((string) $section->getSetting('zone', 'main'));
            $sectionZone = '' !== $sectionZone ? $sectionZone : 'main';
            if ($sectionZone !== $zone) {
                continue;
            }

            $filtered[] = $section;
        }

        return $filtered;
    }

    /**
     * @param iterable<PageSection> $sections
     *
     * @return list<PageSectionRender>
     */
    private function componentRenders(iterable $sections): array
    {
        $renders = [];
        foreach ($sections as $section) {
            if (!$section instanceof PageSection) {
                continue;
            }

            $component = trim((string) $section->getSetting('component'));
            if ('' === $component) {
                continue;
            }

            $renders[] = new PageSectionRender($section, $component);
        }

        return $renders;
    }

    private function normalizeMode(mixed $mode): string
    {
        $mode = trim((string) $mode);

        return \in_array($mode, [self::MODE_GLOBAL, self::MODE_CUSTOM, self::MODE_NONE], true)
            ? $mode
            : self::MODE_GLOBAL;
    }
}
