<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\PageBuilder\Rendering;

use CpCreation\VitiCore\PageBuilder\Builder\BuilderSchemaRegistry;
use CpCreation\VitiCore\PageBuilder\Model\Page;
use CpCreation\VitiCore\PageBuilder\Model\PageSection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

final readonly class PageRenderResolver
{
    private const DEFAULT_TEMPLATES = [
        'builder' => 'page/render/builder.html.twig',
        'edit' => 'page/render/edit.html.twig',
        'full' => 'page/render/full.html.twig',
        'zones' => 'page/render/zones.html.twig',
        'original' => 'page/render/original.html.twig',
    ];

    public function __construct(
        private BuilderSchemaRegistry $builderSchemaRegistry,
        private UrlGeneratorInterface $urlGenerator,
    ) {
    }

    /**
     * @param iterable<PageSection> $sections
     */
    public function resolve(
        Page $page,
        iterable $sections,
        Request $request,
        string $locale,
        bool $editMode,
        bool $builderMode,
        bool $frontEditAccess,
        bool $frontBuilderAccess,
    ): PageRender {
        $pageConfig = $this->builderSchemaRegistry->pageConfig($page->getCode()) ?? [];
        $mode = $this->resolveMode($page, $pageConfig);
        $sectionRenders = $this->sectionRenders($sections);
        $templateKey = $builderMode ? 'builder' : ($editMode ? 'edit' : $mode);

        return new PageRender(
            $page,
            $this->resolveTemplate($pageConfig, $templateKey),
            $mode,
            $editMode,
            $builderMode,
            $frontEditAccess,
            $frontBuilderAccess,
            $sectionRenders,
            $this->zoneRenders($sectionRenders),
            $this->toolbar($request, $editMode, $builderMode, $frontEditAccess, $frontBuilderAccess),
            $this->builderMount($page, $request, $locale, $mode),
            $pageConfig,
        );
    }

    /**
     * @param array<string, mixed> $pageConfig
     */
    private function resolveMode(Page $page, array $pageConfig): string
    {
        $modes = \is_array($pageConfig['modes'] ?? null) ? $pageConfig['modes'] : ['original'];
        $modes = array_values(array_filter(
            array_map(static fn (mixed $mode): string => trim((string) $mode), $modes),
            static fn (string $mode): bool => '' !== $mode,
        ));
        if ([] === $modes) {
            $modes = ['original'];
        }

        $defaultMode = \is_string($pageConfig['defaultMode'] ?? null) ? trim($pageConfig['defaultMode']) : 'original';
        $requestedMode = $page->getSetting('builderMode', $defaultMode);
        $requestedMode = \is_string($requestedMode) ? trim($requestedMode) : $defaultMode;

        return \in_array($requestedMode, $modes, true) ? $requestedMode : 'original';
    }

    /**
     * @param array<string, mixed> $pageConfig
     */
    private function resolveTemplate(array $pageConfig, string $key): string
    {
        $templates = \is_array($pageConfig['templates'] ?? null) ? $pageConfig['templates'] : [];
        $template = $templates[$key] ?? self::DEFAULT_TEMPLATES[$key] ?? self::DEFAULT_TEMPLATES['original'];

        return \is_string($template) && '' !== trim($template) ? trim($template) : self::DEFAULT_TEMPLATES['original'];
    }

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

            $component = $this->sectionComponent($section);
            if (null === $component) {
                continue;
            }

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

        return $renders;
    }

    private function sectionComponent(PageSection $section): ?string
    {
        $component = $this->stringOrNull($section->getSetting('component'));
        if (null === $component) {
            return null;
        }

        return 1 === preg_match('/^[A-Za-z][A-Za-z0-9_]*(?::[A-Za-z][A-Za-z0-9_]*)*$/', $component) ? $component : null;
    }

    /**
     * @param list<PageSectionRender> $sections
     *
     * @return array<string, list<PageSectionRender>>
     */
    private function zoneRenders(array $sections): array
    {
        $zones = [];
        foreach ($sections as $sectionRender) {
            $zone = $this->stringOrNull($sectionRender->section->getSetting('zone')) ?? 'main';
            $zones[$zone] ??= [];
            $zones[$zone][] = $sectionRender;
        }

        return $zones;
    }

    /**
     * @return array<string, string|bool|null>
     */
    private function toolbar(Request $request, bool $editMode, bool $builderMode, bool $frontEditAccess, bool $frontBuilderAccess): array
    {
        $path = $request->getPathInfo();

        return [
            'homeUrl' => $this->urlGenerator->generate('cms_homepage'),
            'editUrl' => $editMode ? $path : $path.'?edit=1',
            'builderUrl' => $builderMode ? $path : $path.'?builder=1',
            'editLabel' => $editMode ? 'Quitter édition' : 'Modifier',
            'builderLabel' => $builderMode ? 'Quitter builder' : 'Builder',
            'showEdit' => $frontEditAccess,
            'showBuilder' => $frontBuilderAccess,
        ];
    }

    /**
     * @return array<string, string>
     */
    private function builderMount(Page $page, Request $request, string $locale, string $mode): array
    {
        return [
            'pageName' => $page->getName(),
            'pageMode' => $mode,
            'loadUrl' => $this->urlGenerator->generate('admin_page_builder_data', ['id' => (string) $page->getId(), 'locale' => $locale]),
            'saveUrl' => $this->urlGenerator->generate('admin_page_builder_save', ['id' => (string) $page->getId(), 'locale' => $locale]),
            'tailwindUrl' => $this->urlGenerator->generate('admin_page_builder_tailwind_classes', ['id' => (string) $page->getId(), 'locale' => $locale]),
            'exitUrl' => $request->getPathInfo(),
        ];
    }

    private function stringOrNull(mixed $value): ?string
    {
        if (null === $value) {
            return null;
        }

        $value = trim((string) $value);

        return '' === $value ? null : $value;
    }
}
