<?php

declare(strict_types=1);

use App\Domain\Common\ValueObject\Email;
use App\Framework\Mail\EmailList;
use App\Framework\Mail\Message;
use App\Framework\Mail\Testing\MockTransport;

describe('MockTransport', function () {
    beforeEach(function () {
        $this->transport = new MockTransport();
        $this->message = new Message(
            from: new Email('sender@example.com'),
            subject: 'Test Subject',
            body: 'Test body',
            to: new EmailList(new Email('recipient@example.com'))
        );
    });

    describe('basic functionality', function () {
        it('sends message successfully by default', function () {
            $result = $this->transport->send($this->message);

            expect($result->isSuccess())->toBeTrue();
            expect($result->getMessageId())->toStartWith('mock_');
            expect($this->transport->getSentMessageCount())->toBe(1);
        });

        it('is available by default', function () {
            expect($this->transport->isAvailable())->toBeTrue();
        });

        it('returns correct name', function () {
            expect($this->transport->getName())->toBe('Mock Transport');
        });
    });

    describe('failure simulation', function () {
        it('can be configured to fail', function () {
            $this->transport->setShouldFail(true, 'Custom failure message');

            $result = $this->transport->send($this->message);

            expect($result->isFailure())->toBeTrue();
            expect($result->getError())->toBe('Custom failure message');
            expect($this->transport->getSentMessageCount())->toBe(0);
        });

        it('uses default failure message when none provided', function () {
            $this->transport->setShouldFail(true);

            $result = $this->transport->send($this->message);

            expect($result->getError())->toBe('Mock transport failure');
        });

        it('can be reset to success mode', function () {
            $this->transport->setShouldFail(true);
            $this->transport->setShouldFail(false);

            $result = $this->transport->send($this->message);

            expect($result->isSuccess())->toBeTrue();
        });
    });

    describe('availability simulation', function () {
        it('can be configured as unavailable', function () {
            $this->transport->setIsAvailable(false);

            expect($this->transport->isAvailable())->toBeFalse();
        });

        it('can be reset to available', function () {
            $this->transport->setIsAvailable(false);
            $this->transport->setIsAvailable(true);

            expect($this->transport->isAvailable())->toBeTrue();
        });
    });

    describe('sent message tracking', function () {
        it('tracks sent messages', function () {
            $message1 = $this->message;
            $message2 = new Message(
                from: new Email('sender@example.com'),
                subject: 'Second Message',
                body: 'Second body',
                to: new EmailList(new Email('recipient2@example.com'))
            );

            $this->transport->send($message1);
            $this->transport->send($message2);

            $sentMessages = $this->transport->getSentMessages();

            expect($sentMessages)->toHaveCount(2);
            expect($sentMessages[0]['message'])->toBe($message1);
            expect($sentMessages[1]['message'])->toBe($message2);
            expect($sentMessages[0]['sent_at'])->toBeInstanceOf(DateTimeImmutable::class);
            expect($sentMessages[1]['sent_at'])->toBeInstanceOf(DateTimeImmutable::class);
        });

        it('gets last sent message', function () {
            $this->transport->send($this->message);

            $lastMessage = $this->transport->getLastSentMessage();

            expect($lastMessage['message'])->toBe($this->message);
            expect($lastMessage['message_id'])->toStartWith('mock_');
        });

        it('returns null for last message when none sent', function () {
            $lastMessage = $this->transport->getLastSentMessage();

            expect($lastMessage)->toBeNull();
        });

        it('clears sent messages', function () {
            $this->transport->send($this->message);
            expect($this->transport->getSentMessageCount())->toBe(1);

            $this->transport->clearSentMessages();

            expect($this->transport->getSentMessageCount())->toBe(0);
            expect($this->transport->getSentMessages())->toBeEmpty();
        });
    });

    describe('recipient checking', function () {
        it('checks if message was sent to specific email', function () {
            $this->transport->send($this->message);

            expect($this->transport->wasSentTo('recipient@example.com'))->toBeTrue();
            expect($this->transport->wasSentTo('other@example.com'))->toBeFalse();
        });

        it('gets messages sent to specific email', function () {
            $message1 = new Message(
                from: new Email('sender@example.com'),
                subject: 'Message 1',
                body: 'Body 1',
                to: new EmailList(new Email('user1@example.com'))
            );

            $message2 = new Message(
                from: new Email('sender@example.com'),
                subject: 'Message 2',
                body: 'Body 2',
                to: new EmailList(new Email('user2@example.com'))
            );

            $message3 = new Message(
                from: new Email('sender@example.com'),
                subject: 'Message 3',
                body: 'Body 3',
                to: new EmailList(new Email('user1@example.com'))
            );

            $this->transport->send($message1);
            $this->transport->send($message2);
            $this->transport->send($message3);

            $messagesForUser1 = $this->transport->getSentMessagesTo('user1@example.com');

            expect($messagesForUser1)->toHaveCount(2);
            expect($messagesForUser1[0]['message'])->toBe($message1);
            expect($messagesForUser1[1]['message'])->toBe($message3);
        });

        it('handles messages with multiple recipients', function () {
            $multiRecipientMessage = new Message(
                from: new Email('sender@example.com'),
                subject: 'Multi-recipient',
                body: 'Body',
                to: EmailList::fromArray([
                    new Email('user1@example.com'),
                    new Email('user2@example.com'),
                ])
            );

            $this->transport->send($multiRecipientMessage);

            expect($this->transport->wasSentTo('user1@example.com'))->toBeTrue();
            expect($this->transport->wasSentTo('user2@example.com'))->toBeTrue();
            expect($this->transport->wasSentTo('user3@example.com'))->toBeFalse();
        });
    });

    describe('subject-based searching', function () {
        it('finds message by subject', function () {
            $message1 = new Message(
                from: new Email('sender@example.com'),
                subject: 'Welcome Email',
                body: 'Welcome!',
                to: new EmailList(new Email('user@example.com'))
            );

            $message2 = new Message(
                from: new Email('sender@example.com'),
                subject: 'Password Reset',
                body: 'Reset your password',
                to: new EmailList(new Email('user@example.com'))
            );

            $this->transport->send($message1);
            $this->transport->send($message2);

            $welcomeMessage = $this->transport->getSentMessageWithSubject('Welcome Email');
            $resetMessage = $this->transport->getSentMessageWithSubject('Password Reset');
            $nonExistent = $this->transport->getSentMessageWithSubject('Non-existent');

            expect($welcomeMessage['message'])->toBe($message1);
            expect($resetMessage['message'])->toBe($message2);
            expect($nonExistent)->toBeNull();
        });
    });

    describe('batch sending', function () {
        it('sends batch of valid messages', function () {
            $messages = [
                new Message(
                    from: new Email('sender@example.com'),
                    subject: 'Message 1',
                    body: 'Body 1',
                    to: new EmailList(new Email('user1@example.com'))
                ),
                new Message(
                    from: new Email('sender@example.com'),
                    subject: 'Message 2',
                    body: 'Body 2',
                    to: new EmailList(new Email('user2@example.com'))
                ),
            ];

            $results = $this->transport->sendBatch($messages);

            expect($results)->toHaveCount(2);
            expect($results[0]->isSuccess())->toBeTrue();
            expect($results[1]->isSuccess())->toBeTrue();
            expect($this->transport->getSentMessageCount())->toBe(2);
        });

        it('handles invalid messages in batch', function () {
            $batch = [
                new Message(
                    from: new Email('sender@example.com'),
                    subject: 'Valid Message',
                    body: 'Body',
                    to: new EmailList(new Email('user@example.com'))
                ),
                'invalid-message',
                new Message(
                    from: new Email('sender@example.com'),
                    subject: 'Another Valid Message',
                    body: 'Body',
                    to: new EmailList(new Email('user2@example.com'))
                ),
            ];

            $results = $this->transport->sendBatch($batch);

            expect($results)->toHaveCount(3);
            expect($results[0]->isSuccess())->toBeTrue();
            expect($results[1]->isFailure())->toBeTrue();
            expect($results[1]->getError())->toBe('Invalid message at index 1');
            expect($results[2]->isSuccess())->toBeTrue();
            expect($this->transport->getSentMessageCount())->toBe(2);
        });
    });
});
