<?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\Cart;

use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\User\Model\User;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMSetup;
use Doctrine\Persistence\ManagerRegistry;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Doctrine\UuidType;
use Ramsey\Uuid\Uuid;

final class GuestCustomerRepositoryTest extends TestCase
{
    private Connection $connection;
    private CartRepository $repository;

    protected function setUp(): void
    {
        if (!Type::hasType('uuid')) {
            Type::addType('uuid', UuidType::class);
        }

        $configuration = ORMSetup::createAttributeMetadataConfiguration([
            \dirname(__DIR__, 2).'/src/Cart/Model',
            \dirname(__DIR__, 2).'/src/User/Model',
        ], true);
        $this->connection = DriverManager::getConnection([
            'driver' => 'pdo_sqlite',
            'memory' => true,
        ], $configuration);
        $entityManager = new EntityManager($this->connection, $configuration);
        $registry = $this->createMock(ManagerRegistry::class);
        $registry->method('getManagerForClass')
            ->with(Cart::class)
            ->willReturn($entityManager);

        $this->repository = new CartRepository($registry);
        $this->createSchema();
    }

    public function testItGroupsCompletedGuestOrdersByNormalizedEmail(): void
    {
        $this->insertGuestOrder(
            email: ' CLIENT@example.com ',
            firstname: 'Ancien',
            lastname: 'Nom',
            orderedAt: '2025-04-12 09:00:00',
        );
        $latestCartId = $this->insertGuestOrder(
            email: 'client@EXAMPLE.com',
            firstname: 'Nouveau',
            lastname: 'Nom',
            payedAt: '2026-08-20 14:30:00',
        );
        $this->insertGuestOrder(
            email: 'autre@example.com',
            firstname: 'Autre',
            lastname: 'Client',
            payedAt: '2026-07-01 12:00:00',
        );

        $customers = $this->repository->findUnregisteredCustomerOrderSummaries();

        self::assertCount(2, $customers);
        self::assertSame('client@example.com', $customers[0]['email']);
        self::assertSame('Nouveau', $customers[0]['firstname']);
        self::assertSame('Nom', $customers[0]['lastname']);
        self::assertSame(2, $customers[0]['order_count']);
        self::assertSame('2025-04-12 09:00:00', $customers[0]['first_order_at']->format('Y-m-d H:i:s'));
        self::assertSame('2026-08-20 14:30:00', $customers[0]['last_order_at']->format('Y-m-d H:i:s'));
        self::assertSame($latestCartId, $customers[0]['representative_cart_id']);
        self::assertSame('autre@example.com', $customers[1]['email']);
        self::assertSame(1, $customers[1]['order_count']);
    }

    public function testItExcludesRegisteredCustomersAndNonOrders(): void
    {
        $registeredEmail = 'compte@example.com';
        $registeredUser = new User();
        $registeredUser->setEmail($registeredEmail);
        $this->connection->insert('user_user', [
            'id' => $registeredUser->getId()->toString(),
            'email' => mb_strtoupper($registeredEmail),
        ]);

        $this->insertGuestOrder(email: $registeredEmail, payedAt: '2026-08-20 10:00:00');
        $this->insertGuestOrder(email: 'lie@example.com', payedAt: '2026-08-20 11:00:00', userId: Uuid::uuid4()->toString());
        $this->insertGuestOrder(email: 'abandon@example.com');
        $this->insertGuestOrder(email: 'vide@example.com', payedAt: '2026-08-20 12:00:00', withItem: false);
        $this->insertGuestOrder(email: 'desactive@example.com', payedAt: '2026-08-20 13:00:00', checkoutDisabled: true);
        $this->insertGuestOrder(email: 'supprime@example.com', payedAt: '2026-08-20 14:00:00', deleted: true);
        $this->insertGuestOrder(email: 'conserve@example.com', orderedAt: '2026-08-20 15:00:00');

        $customers = $this->repository->findUnregisteredCustomerOrderSummaries();

        self::assertCount(1, $customers);
        self::assertSame('conserve@example.com', $customers[0]['email']);
        self::assertSame(
            [$registeredUser->getId()->toString()],
            $this->repository->findUserIdsWithOrders([$registeredUser]),
        );
        self::assertNull($this->repository->findUnregisteredCustomerOrderHistory(Uuid::uuid4()->toString()));
    }

    private function createSchema(): void
    {
        foreach ([
            'CREATE TABLE cart_cart (id TEXT PRIMARY KEY, user_id TEXT NULL, invoice_address_id TEXT NULL, delivery_address_id TEXT NULL, deleted_at TEXT NULL)',
            'CREATE TABLE cart_cart_checkout (id TEXT PRIMARY KEY, cart_cart_id TEXT NOT NULL, payed_at TEXT NULL, ordered_at TEXT NULL, payedAt TEXT NULL, orderedAt TEXT NULL, disabled INTEGER NOT NULL DEFAULT 0, deleted_at TEXT NULL)',
            'CREATE TABLE cart_cart_item (id TEXT PRIMARY KEY, cart_cart_id TEXT NOT NULL)',
            'CREATE TABLE cart_invoice_address (id TEXT PRIMARY KEY, email TEXT NULL, firstname TEXT NULL, lastname TEXT NULL)',
            'CREATE TABLE cart_delivery_address (id TEXT PRIMARY KEY, email TEXT NULL, firstname TEXT NULL, lastname TEXT NULL)',
            'CREATE TABLE user_user (id TEXT PRIMARY KEY, email TEXT NULL)',
        ] as $sql) {
            $this->connection->executeStatement($sql);
        }
    }

    private function insertGuestOrder(
        string $email,
        ?string $firstname = 'Jean',
        ?string $lastname = 'Client',
        ?string $payedAt = null,
        ?string $orderedAt = null,
        ?string $userId = null,
        bool $withItem = true,
        bool $checkoutDisabled = false,
        bool $deleted = false,
    ): string {
        $cartId = Uuid::uuid4()->toString();
        $invoiceAddressId = Uuid::uuid4()->toString();
        $deliveryAddressId = Uuid::uuid4()->toString();

        $this->connection->insert('cart_invoice_address', [
            'id' => $invoiceAddressId,
            'email' => $email,
            'firstname' => $firstname,
            'lastname' => $lastname,
        ]);
        $this->connection->insert('cart_delivery_address', [
            'id' => $deliveryAddressId,
            'email' => $email,
            'firstname' => $firstname,
            'lastname' => $lastname,
        ]);
        $this->connection->insert('cart_cart', [
            'id' => $cartId,
            'user_id' => $userId,
            'invoice_address_id' => $invoiceAddressId,
            'delivery_address_id' => $deliveryAddressId,
            'deleted_at' => $deleted ? '2026-08-21 10:00:00' : null,
        ]);
        $this->connection->insert('cart_cart_checkout', [
            'id' => Uuid::uuid4()->toString(),
            'cart_cart_id' => $cartId,
            'payed_at' => $payedAt,
            'ordered_at' => $orderedAt,
            'payedAt' => $payedAt,
            'orderedAt' => $orderedAt,
            'disabled' => $checkoutDisabled ? 1 : 0,
            'deleted_at' => $deleted ? '2026-08-21 10:00:00' : null,
        ]);

        if ($withItem) {
            $this->connection->insert('cart_cart_item', [
                'id' => Uuid::uuid4()->toString(),
                'cart_cart_id' => $cartId,
            ]);
        }

        return $cartId;
    }
}
