<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Tests\Commerce\Catalog;

use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Commerce\Catalog\CatalogProductReviewsProjector;
use CpCreation\VitiCore\Marketing\Model\ProductReview;
use CpCreation\VitiCore\Marketing\Model\ReviewInvitation;
use CpCreation\VitiCore\Marketing\ProductReviewConfigurationProvider;
use CpCreation\VitiCore\Marketing\PublishedProductReviewsProvider;
use CpCreation\VitiCore\Product\Model\Product;
use PHPUnit\Framework\TestCase;

final class CatalogProductReviewsProjectorTest extends TestCase
{
    public function testDisabledReviewsExposeOnlyTheClearingState(): void
    {
        $reviewsProvider = new class implements PublishedProductReviewsProvider {
            public function findPublishedByProduct(
                Product $product,
                ?\DateTimeImmutable $now = null,
            ): array {
                throw new \LogicException('Les avis désactivés ne doivent pas être lus.');
            }

            public function findPublishedByProducts(
                iterable $products,
                ?\DateTimeImmutable $now = null,
            ): array {
                throw new \LogicException('Les avis désactivés ne doivent pas être lus.');
            }
        };

        $projection = new CatalogProductReviewsProjector(
            $this->configuration(enabled: false),
            $reviewsProvider,
        )->project(new Product());

        self::assertFalse($projection->enabled);
        self::assertSame(0, $projection->review_count);
        self::assertNull($projection->average_rating);
        self::assertSame([], $projection->items);
    }

    public function testEnabledReviewsExposeOnlyTheirPublicDisplayData(): void
    {
        $product = new Product();
        $first = $this->review($product, 5, 'Alice', 'Excellent', 'Très belle découverte.', '2026-07-22T10:00:00+02:00');
        $first->reply('Merci pour votre retour.');
        $second = $this->review($product, 4, 'Bruno', null, 'Très bon produit.', '2026-07-21T09:00:00+02:00');
        $reviewsProvider = new class ([$first, $second]) implements PublishedProductReviewsProvider {
            /** @param list<ProductReview> $reviews */
            public function __construct(private readonly array $reviews) {}

            public function findPublishedByProduct(
                Product $product,
                ?\DateTimeImmutable $now = null,
            ): array {
                return $this->reviews;
            }

            public function findPublishedByProducts(
                iterable $products,
                ?\DateTimeImmutable $now = null,
            ): array {
                return $this->reviews;
            }
        };

        $projection = new CatalogProductReviewsProjector(
            $this->configuration(enabled: true),
            $reviewsProvider,
        )->project($product);

        self::assertTrue($projection->enabled);
        self::assertSame(2, $projection->review_count);
        self::assertSame(4.5, $projection->average_rating);
        self::assertCount(2, $projection->items);
        self::assertSame('Alice', $projection->items[0]->author_display_name);
        self::assertSame('Excellent', $projection->items[0]->title);
        self::assertSame('Très belle découverte.', $projection->items[0]->comment);
        self::assertTrue($projection->items[0]->verified_purchase);
        self::assertSame('2026-07-22T08:00:00+00:00', $projection->items[0]->published_at);
        self::assertSame('Merci pour votre retour.', $projection->items[0]->merchant_reply);
    }

    private function configuration(bool $enabled): ProductReviewConfigurationProvider
    {
        return new class ($enabled) implements ProductReviewConfigurationProvider {
            public function __construct(private readonly bool $enabled) {}

            public function isEnabled(): bool
            {
                return $this->enabled;
            }

            public function isModerationRequired(): bool
            {
                return true;
            }
        };
    }

    private function review(
        Product $product,
        int $rating,
        string $author,
        ?string $title,
        string $comment,
        string $submittedAt,
    ): ProductReview {
        $date = new \DateTimeImmutable($submittedAt);
        $invitation = new ReviewInvitation(
            new Cart(),
            'client@example.test',
            hash('sha256', $author),
            $date->modify('-1 day'),
            $date->modify('+90 days'),
        );

        return new ProductReview(
            $invitation,
            $product,
            null,
            $rating,
            $author,
            $title,
            $comment,
            false,
            $date,
        );
    }
}
