<?php

declare(strict_types=1);

/*
 * 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\Model\WithdrawalRequest;
use PHPUnit\Framework\TestCase;

final class WithdrawalRequestTest extends TestCase
{
    public function testItKeepsAnImmutableSnapshotOfTheOnlineStatement(): void
    {
        $cart = new Cart();
        $submittedAt = new \DateTimeImmutable('2026-06-19 14:35:12 Europe/Paris');

        $request = new WithdrawalRequest(
            cart: $cart,
            firstname: '  Jeanne ',
            lastname: ' Dupont  ',
            contractReference: ' CMD-123 ',
            receiptEmail: ' Jeanne.Dupont@Example.test ',
            orderReceived: true,
            receivedAt: new \DateTimeImmutable('2026-06-18'),
            locale: 'fr',
            ipAddress: '192.0.2.1',
            userAgent: 'Browser test',
            submittedAt: $submittedAt,
            reasonCode: WithdrawalRequest::REASON_CHANGE_OF_MIND,
            comment: '  Je souhaite préciser ma demande.  ',
        );

        self::assertSame($request, $cart->getWithdrawalRequest());
        self::assertSame('Jeanne', $request->getFirstname());
        self::assertSame('Dupont', $request->getLastname());
        self::assertSame('CMD-123', $request->getContractReference());
        self::assertSame('jeanne.dupont@example.test', $request->getReceiptEmail());
        self::assertTrue($request->isOrderReceived());
        self::assertSame('2026-06-18', $request->getReceivedAt()?->format('Y-m-d'));
        self::assertSame($submittedAt, $request->getSubmittedAt());
        self::assertSame('192.0.2.1', $request->getIpAddress());
        self::assertSame('Browser test', $request->getUserAgent());
        self::assertSame('fr', $request->getLocale());
        self::assertSame(WithdrawalRequest::REASON_CHANGE_OF_MIND, $request->getReasonCode());
        self::assertSame('Je souhaite préciser ma demande.', $request->getComment());
    }

    public function testItTracksAcknowledgementAndProcessingTimes(): void
    {
        $request = new WithdrawalRequest(
            cart: new Cart(),
            firstname: 'Jeanne',
            lastname: 'Dupont',
            contractReference: 'CMD-123',
            receiptEmail: 'jeanne@example.test',
            orderReceived: false,
            locale: 'fr',
        );
        $acknowledgedAt = new \DateTimeImmutable('2026-06-19 14:36:00 Europe/Paris');
        $processedAt = new \DateTimeImmutable('2026-06-20 09:00:00 Europe/Paris');

        $request->markAcknowledged($acknowledgedAt);
        $request->markProcessed($processedAt);

        self::assertSame($acknowledgedAt, $request->getAcknowledgedAt());
        self::assertSame($processedAt, $request->getProcessedAt());
    }

    public function testItDiscardsAReceptionDateWhenTheOrderWasNotReceived(): void
    {
        $request = new WithdrawalRequest(
            cart: new Cart(),
            firstname: 'Jeanne',
            lastname: 'Dupont',
            contractReference: 'CMD-123',
            receiptEmail: 'jeanne@example.test',
            orderReceived: false,
            receivedAt: new \DateTimeImmutable('2026-06-18'),
            locale: 'fr',
        );

        self::assertFalse($request->isOrderReceived());
        self::assertNull($request->getReceivedAt());
    }

    public function testItRejectsAnUnknownReasonCode(): void
    {
        $this->expectException(\InvalidArgumentException::class);

        new WithdrawalRequest(
            cart: new Cart(),
            firstname: 'Jeanne',
            lastname: 'Dupont',
            contractReference: 'CMD-123',
            receiptEmail: 'jeanne@example.test',
            orderReceived: false,
            locale: 'fr',
            reasonCode: 'unknown_reason',
        );
    }

    public function testItNormalizesAnEmptyComment(): void
    {
        $request = new WithdrawalRequest(
            cart: new Cart(),
            firstname: 'Jeanne',
            lastname: 'Dupont',
            contractReference: 'CMD-123',
            receiptEmail: 'jeanne@example.test',
            orderReceived: false,
            locale: 'fr',
            comment: '   ',
        );

        self::assertNull($request->getComment());
    }

    public function testItRejectsACommentThatIsTooLong(): void
    {
        $this->expectException(\InvalidArgumentException::class);

        new WithdrawalRequest(
            cart: new Cart(),
            firstname: 'Jeanne',
            lastname: 'Dupont',
            contractReference: 'CMD-123',
            receiptEmail: 'jeanne@example.test',
            orderReceived: false,
            locale: 'fr',
            comment: str_repeat('a', WithdrawalRequest::COMMENT_MAX_LENGTH + 1),
        );
    }
}
