<?php

declare(strict_types=1);

use App\Domain\Common\ValueObject\Email;
use App\Framework\Mail\EmailList;

describe('EmailList', function () {
    it('creates empty email list', function () {
        $list = new EmailList();

        expect($list->isEmpty())->toBeTrue();
        expect($list->isNotEmpty())->toBeFalse();
        expect($list->count())->toBe(0);
        expect($list->toArray())->toBeEmpty();
    });

    it('creates email list with emails', function () {
        $email1 = new Email('user1@example.com');
        $email2 = new Email('user2@example.com');

        $list = new EmailList($email1, $email2);

        expect($list->isEmpty())->toBeFalse();
        expect($list->isNotEmpty())->toBeTrue();
        expect($list->count())->toBe(2);
        expect($list->toArray())->toHaveCount(2);
        expect($list->toArray()[0]->value)->toBe('user1@example.com');
        expect($list->toArray()[1]->value)->toBe('user2@example.com');
    });

    it('creates email list from strings', function () {
        $list = new EmailList('user1@example.com', 'user2@example.com');

        expect($list->count())->toBe(2);
        expect($list->toArray()[0]->value)->toBe('user1@example.com');
        expect($list->toArray()[1]->value)->toBe('user2@example.com');
    });

    it('creates email list from array using fromArray', function () {
        $emails = [
            new Email('user1@example.com'),
            new Email('user2@example.com'),
        ];

        $list = EmailList::fromArray($emails);

        expect($list->count())->toBe(2);
        expect($list->toArray())->toBe($emails);
    });

    it('converts to string representation', function () {
        $list = new EmailList('user1@example.com', 'user2@example.com');

        expect($list->toString())->toBe('user1@example.com, user2@example.com');
    });

    it('handles single email in toString', function () {
        $list = new EmailList('single@example.com');

        expect($list->toString())->toBe('single@example.com');
    });

    it('handles empty list in toString', function () {
        $list = new EmailList();

        expect($list->toString())->toBe('');
    });

    it('adds email to list', function () {
        $list = new EmailList('existing@example.com');

        $newList = $list->add('new@example.com');

        expect($list->count())->toBe(1); // Original unchanged
        expect($newList->count())->toBe(2); // New list has both
        expect($newList->toArray()[1]->value)->toBe('new@example.com');
    });

    it('merges two email lists', function () {
        $list1 = new EmailList('user1@example.com');
        $list2 = new EmailList('user2@example.com');

        $merged = $list1->merge($list2);

        expect($list1->count())->toBe(1); // Original unchanged
        expect($list2->count())->toBe(1); // Original unchanged
        expect($merged->count())->toBe(2);
        expect($merged->toArray()[0]->value)->toBe('user1@example.com');
        expect($merged->toArray()[1]->value)->toBe('user2@example.com');
    });

    it('removes duplicates', function () {
        $list = new EmailList('user@example.com', 'user@example.com', 'other@example.com');
        $uniqueList = $list->unique();

        expect($list->count())->toBe(3); // Original unchanged
        expect($uniqueList->count())->toBe(2); // Duplicates removed
        expect($uniqueList->toArray()[0]->value)->toBe('user@example.com');
        expect($uniqueList->toArray()[1]->value)->toBe('other@example.com');
    });

    it('filters emails with callback', function () {
        $list = new EmailList('user@gmail.com', 'admin@company.com', 'test@gmail.com');

        $gmailList = $list->filter(fn (Email $email) => str_contains($email->value, 'gmail.com'));

        expect($list->count())->toBe(3); // Original unchanged
        expect($gmailList->count())->toBe(2);
        expect($gmailList->toArray()[0]->value)->toBe('user@gmail.com');
        expect($gmailList->toArray()[1]->value)->toBe('test@gmail.com');
    });

    it('is iterable', function () {
        $list = new EmailList('user1@example.com', 'user2@example.com');

        $iteratedEmails = [];
        foreach ($list as $email) {
            $iteratedEmails[] = $email->value;
        }

        expect($iteratedEmails)->toBe(['user1@example.com', 'user2@example.com']);
    });

    it('is countable', function () {
        $list = new EmailList('user1@example.com', 'user2@example.com');

        expect(count($list))->toBe(2);
    });

    it('checks if email exists in list', function () {
        $email1 = new Email('existing@example.com');
        $list = new EmailList($email1);

        expect($list->contains($email1))->toBeTrue();
        expect($list->contains('existing@example.com'))->toBeTrue();
        expect($list->contains('nonexistent@example.com'))->toBeFalse();
    });

    it('gets first email', function () {
        $list = new EmailList('first@example.com', 'second@example.com');

        expect($list->first()->value)->toBe('first@example.com');
    });

    it('returns null for first email in empty list', function () {
        $list = new EmailList();

        expect($list->first())->toBeNull();
    });

    it('gets last email', function () {
        $list = new EmailList('first@example.com', 'last@example.com');

        expect($list->last()->value)->toBe('last@example.com');
    });

    it('returns null for last email in empty list', function () {
        $list = new EmailList();

        expect($list->last())->toBeNull();
    });

    it('converts to string array', function () {
        $list = new EmailList('user1@example.com', 'user2@example.com');

        expect($list->toStringArray())->toBe(['user1@example.com', 'user2@example.com']);
    });

    it('supports custom separator in toString', function () {
        $list = new EmailList('user1@example.com', 'user2@example.com');

        expect($list->toString('; '))->toBe('user1@example.com; user2@example.com');
    });
});
