<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Tests\Twig;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Legal\Page;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Shop\Vacation;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\LegalContentRenderer;
use CpCreation\VitiCore\Contact\Model\Contact;
use CpCreation\VitiCore\Contact\Repository\ContactRepository;
use CpCreation\VitiCore\Shop\Model\ShopConfig;
use PHPUnit\Framework\TestCase;
use Symfony\UX\TwigComponent\ComponentAttributes;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use Twig\Runtime\EscaperRuntime;
use Twig\TwigFilter;
use Twig\TwigFunction;

final class ComponentTemplateTest extends TestCase
{
    public function testImageKeepsIntrinsicDimensionsWithoutLayoutDefaults(): void
    {
        $html = $this->render('Image.html.twig', [
            'attributes' => $this->attributes([
                'class' => 'image-shell',
                'img:class' => 'image-element object-contain',
            ]),
            'path' => 'images/logo.svg',
            'local' => true,
            'alt' => 'Logo',
            'imageWidth' => 320,
            'imageHeight' => 180,
        ]);

        self::assertStringContainsString('class="image-shell"', $html);
        self::assertStringContainsString('class="image-element object-contain"', $html);
        self::assertStringContainsString('width="320"', $html);
        self::assertStringContainsString('height="180"', $html);
        self::assertStringNotContainsString('h-full', $html);
        self::assertStringNotContainsString('w-full', $html);
        self::assertStringNotContainsString('object-cover', $html);
    }

    public function testImageKeepsOnlyLazyLoadingBehaviorAsDefaultClass(): void
    {
        $html = $this->render('Image.html.twig', [
            'attributes' => $this->attributes([
                'class' => 'image-shell',
                'row:class' => 'w-full',
                'img:class' => 'h-auto w-full object-contain',
            ]),
            'path' => 'images/photo.jpg',
            'local' => false,
            'responsive' => false,
            'lazy' => true,
            'fm' => 'webp',
            'fit' => 'contain',
            'crop' => null,
            'quality' => 80,
            'bgAlpha' => null,
            'alt' => 'Photo',
            'imageWidth' => 640,
            'imageHeight' => 480,
            'mapping' => 'image',
        ]);

        self::assertStringContainsString('class="relative w-full"', $html);
        self::assertStringContainsString('class="opacity-0 transition-opacity duration-100 h-auto w-full object-contain"', $html);
        self::assertStringContainsString('width="640"', $html);
        self::assertStringContainsString('height="480"', $html);
        self::assertStringNotContainsString('h-full', $html);
    }

    public function testNotificationMergesRootAndNestedClasses(): void
    {
        $html = $this->render('Notification.html.twig', [
            'attributes' => $this->attributes([
                'class' => 'custom-toast',
                'wrapper:class' => 'custom-icon',
                'message:class' => 'custom-message',
                'close:class' => 'custom-close',
            ]),
            'level' => 'success',
            'message' => 'Enregistré',
        ]);

        self::assertStringContainsString('animate-toast flex w-full', $html);
        self::assertStringContainsString('custom-toast', $html);
        self::assertStringContainsString('custom-icon', $html);
        self::assertStringContainsString('custom-message', $html);
        self::assertStringContainsString('custom-close', $html);

        preg_match_all('/<(?:div|button)\b[^>]*>/', $html, $tags);
        foreach ($tags[0] as $tag) {
            self::assertSame(1, mb_substr_count($tag, 'class='), $tag);
        }
    }

    public function testLegalPageRendersContentLoadedFromDatabase(): void
    {
        $contact = new Contact();
        $contact->setCompany('Maison Exemple');
        $contact->setLegalNoticeContent('<h2>Mentions enregistrées en base</h2><p>{{ contact.company }}</p>');

        $repository = $this->createMock(ContactRepository::class);
        $repository->expects(self::once())
            ->method('findOne')
            ->willReturn($contact);

        $page = new Page($repository, new LegalContentRenderer());
        $page->type = Page::TYPE_LEGAL;
        $page->postMount();

        $html = $this->render('legal/Page.html.twig', [
            'attributes' => $this->attributes([]),
            'this' => $page,
        ]);

        self::assertStringContainsString('<h2>Mentions enregistrées en base</h2>', $html);
        self::assertStringContainsString('<p>Maison Exemple</p>', $html);
    }

    public function testPrivacyPolicyPageRendersContentLoadedFromDatabase(): void
    {
        $contact = new Contact();
        $contact->setPrivacyPolicyContent('<h2>Politique enregistrée en base</h2><p>Contenu confidentiel.</p>');

        $repository = $this->createMock(ContactRepository::class);
        $repository->expects(self::once())
            ->method('findOne')
            ->willReturn($contact);

        $page = new Page($repository, new LegalContentRenderer());
        $page->type = Page::TYPE_PRIVACY_POLICY;
        $page->postMount();

        $html = $this->render('legal/Page.html.twig', [
            'attributes' => $this->attributes([]),
            'this' => $page,
        ]);

        self::assertStringContainsString('<h2>Politique enregistrée en base</h2>', $html);
        self::assertStringContainsString('<p>Contenu confidentiel.</p>', $html);
    }

    public function testVacationRendersConfiguredMessageDuringActivePeriod(): void
    {
        $config = new ShopConfig();
        $config->setCurrentLocale('fr');
        $config->translate('fr')->setVacation('<p>Fermeture estivale</p>');
        $config->setVacationOf(new \DateTime('2020-01-01 00:00:00'));
        $config->setVacationAt(new \DateTime('2100-01-01 00:00:00'));

        $vacation = new Vacation();
        $vacation->config = $config;

        $html = $this->render('shop/Vacation.html.twig', [
            'attributes' => $this->attributes([]),
            'config' => $config,
            'template' => null,
            'this' => $vacation,
        ]);

        self::assertStringContainsString('role="status"', $html);
        self::assertStringContainsString('<p>Fermeture estivale</p>', $html);
    }

    public function testVacationDoesNotRenderOutsideConfiguredPeriod(): void
    {
        $config = new ShopConfig();
        $config->setCurrentLocale('fr');
        $config->translate('fr')->setVacation('<p>Fermeture estivale</p>');
        $config->setVacationOf(new \DateTime('2020-01-01 00:00:00'));
        $config->setVacationAt(new \DateTime('2020-02-01 00:00:00'));

        $vacation = new Vacation();
        $vacation->config = $config;

        $html = $this->render('shop/Vacation.html.twig', [
            'attributes' => $this->attributes([]),
            'config' => $config,
            'template' => null,
            'this' => $vacation,
        ]);

        self::assertSame('', mb_trim($html));
    }

    public function testCollectionCatalogDoesNotRenderWithoutPublishedCollection(): void
    {
        $catalog = new class {
            public function isCollectionSource(): bool
            {
                return true;
            }

            public function getProductCollection(): null
            {
                return null;
            }
        };

        $html = $this->render('shop/Catalog.html.twig', [
            'this' => $catalog,
        ]);

        self::assertSame('', mb_trim($html));
    }

    /**
     * @param array<string, mixed> $context
     */
    private function render(string $template, array $context): string
    {
        $componentsDirectory = \dirname(__DIR__, 2) . '/src/Bridge/Symfony/Bundle/Resources/views/components';
        $loader = new FilesystemLoader($componentsDirectory);
        $loader->addPath($componentsDirectory, 'components');
        $twig = new Environment($loader, ['autoescape' => false]);
        $twig->addFunction(new TwigFunction('asset_glide', static fn(): string => '/generated/image'));
        $twig->addFunction(new TwigFunction('component', static fn(): string => ''));
        $twig->addFunction(new TwigFunction('path', static fn(string $route): string => '/' . $route));
        $twig->addFilter(new TwigFilter('trans', static fn(string $key): string => $key));

        return $twig->render($template, $context);
    }

    /**
     * @param array<string, string|bool> $attributes
     */
    private function attributes(array $attributes): ComponentAttributes
    {
        return new ComponentAttributes($attributes, new EscaperRuntime());
    }
}
