<?php
declare(strict_types=1);

namespace Tests\SmtpClient;

use FreshCloud\Mail\Smtp\InMemorySmtpClient;
use FreshCloud\Mail\SmtpClient\SmtpCredentials;
use FreshCloud\Mail\SmtpClient\SmtpMessage;
use FreshCloud\Mail\SmtpClient\SmtpResult;
use FreshCloud\Mail\SmtpClient\SmtpConnectionResult;
use FreshCloud\Mail\Log\Logger;
use FreshCloud\Mail\PocketBaseAdapter\PocketBaseAdapter;
use PHPUnit\Framework\TestCase;
use InvalidArgumentException;

final class InMemorySmtpClientTest extends TestCase
{
    private InMemorySmtpClient $client;
    private PocketBaseAdapter $pb;
    private Logger $logger;

    protected function setUp(): void
    {
        $this->pb = $this->createMock(PocketBaseAdapter::class);
        $this->logger = $this->createMock(Logger::class);
        $this->client = new InMemorySmtpClient($this->pb, $this->logger);
    }

    public function testValidConnection(): void
    {
        $creds = new SmtpCredentials(
            host: 'smtp.example.com',
            port: 587,
            username: 'user',
            password: 'pass',
            encryption: 'tls',
            userId: 'user1',
            accountId: 'acc1'
        );

        $result = $this->client->connect($creds);
        $this->assertTrue($result->success);
        $this->assertContains('AUTH LOGIN', $result->capabilities);
    }

    public function testInvalidPortRejected(): void
    {
        $this->expectException(InvalidArgumentException::class);
        $creds = new SmtpCredentials(
            host: 'smtp.example.com',
            port: 0,
            username: 'user',
            password: 'pass',
            encryption: 'tls',
            userId: 'user1',
            accountId: 'acc1'
        );
    }

    public function testSendWithNoRecipientsFails(): void
    {
        $this->expectException(InvalidArgumentException::class);
        $msg = new SmtpMessage(
            from: 'test@example.com',
            to: [],
            subject: 'Test',
            body: 'Body'
        );
        $this->client->send($msg, $this->getValidCredentials());
    }

    public function testSendValidMessageQueuesOnFailure(): void
    {
        $msg = new SmtpMessage(
            from: 'test@example.com',
            to: ['user@example.com'],
            subject: 'Test',
            body: 'Body'
        );
        $creds = $this->getValidCredentials();
        $this->client->connect($creds);

        $result = $this->client->send($msg, $creds);
        $this->assertContains($result->status, ['sent', 'queued', 'failed']);
    }

    public function testSendFailureQueuesOrFails(): void
    {
        // Use a pb that throws on collection()
        $badPb = new class implements PocketBaseAdapter {
            public function collection(string $name): \FreshCloud\Mail\PocketBaseAdapter\CollectionAdapter {
                throw new \RuntimeException('PB down');
            }
        };
        $client = new InMemorySmtpClient($badPb, $this->logger);

        $msg = new SmtpMessage(
            from: 'test@example.com',
            to: ['user@example.com'],
            subject: 'Test',
            body: 'Body'
        );
        $creds = $this->getValidCredentials();
        $client->connect($creds);

        $seen_failure = false;
        for ($i = 0; $i < 50 && !$seen_failure; $i++) {
            $r = $client->send($msg, $creds);
            if ($r->status === 'failed') {
                $this->assertNotNull($r->error);
                $seen_failure = true;
            }
        }
        $this->assertTrue($seen_failure, 'Expected at least one failure in 50 attempts');
    }

    public function testDisconnectClearsPool(): void
    {
        $creds = $this->getValidCredentials();
        $this->client->connect($creds);
        $this->client->disconnect();
        
        $reflection = new \ReflectionClass($this->client);
        $pool = $reflection->getProperty('connectionPool');
        $pool->setAccessible(true);
        $this->assertEmpty($pool->getValue($this->client));
    }

    public function testGetQueueCountReturnsInt(): void
    {
        $this->client->connect($this->getValidCredentials());
        $count = $this->client->getQueueCount('user1', 'acc1');
        $this->assertIsInt($count);
        $this->assertEquals(0, $count);
    }

    public function testMessageWithoutSubjectRejected(): void
    {
        $this->expectException(InvalidArgumentException::class);
        $msg = new SmtpMessage(
            from: 'test@example.com',
            to: ['user@example.com'],
            subject: '',
            body: 'Body'
        );
        $this->client->send($msg, $this->getValidCredentials());
    }

    private function getValidCredentials(): SmtpCredentials
    {
        return new SmtpCredentials(
            host: 'smtp.example.com',
            port: 587,
            username: 'user',
            password: 'pass',
            encryption: 'tls',
            userId: 'user1',
            accountId: 'acc1'
        );
    }
}