<?php

declare(strict_types=1);

use App\Domain\Common\ValueObject\Email;
use App\Framework\Email\CssInliner;
use App\Framework\Email\Emails\PasswordResetEmail;
use App\Framework\Email\Emails\WelcomeEmail;
use App\Framework\Email\EmailService;
use App\Framework\Email\EmailTemplateRenderer;
use App\Framework\Http\Url\UrlFactory;
use App\Framework\Logging\Logger;
use App\Framework\Mail\MailerInterface;
use App\Framework\Mail\Priority;
use App\Framework\Template\Parser\DomTemplateParser;

beforeEach(function () {
    // Create test templates directory
    $templatesDir = __DIR__ . '/../../tmp/email-templates';
    if (! is_dir($templatesDir)) {
        mkdir($templatesDir, 0755, true);
    }

    // Create welcome template
    $welcomeTemplate = <<<HTML
    <!DOCTYPE html>
    <html>
    <head>
        <style>
            .header { background: #007bff; color: white; padding: 20px; }
            .content { padding: 20px; }
            .button { background: #28a745; color: white; padding: 10px 20px; text-decoration: none; }
        </style>
        <title>Welcome to {{company_name}}</title>
    </head>
    <body>
        <div class="header">
            <h1>Willkommen bei {{company_name}}!</h1>
        </div>
        <div class="content">
            <p>Hallo {{name}},</p>
            <p>willkommen bei {{company_name}}! Wir freuen uns, dass du dabei bist.</p>
            <a href="{{login_url}}" class="button">Jetzt anmelden</a>
            <p>&copy; {{current_year}} {{company_name}}</p>
        </div>
    </body>
    </html>
    HTML;

    file_put_contents($templatesDir . '/welcome.html', $welcomeTemplate);

    // Create password reset template
    $passwordResetTemplate = <<<HTML
    <!DOCTYPE html>
    <html>
    <head>
        <style>
            .security { background: #dc3545; color: white; padding: 15px; }
            .content { padding: 20px; }
            .reset-button { background: #007bff; color: white; padding: 12px 24px; text-decoration: none; }
            .warning { background: #fff3cd; padding: 10px; margin: 10px 0; }
        </style>
        <title>Passwort zurücksetzen - Aktion erforderlich</title>
    </head>
    <body>
        <div class="security">
            <h1>Passwort zurücksetzen</h1>
        </div>
        <div class="content">
            <p>Hallo {{name}},</p>
            <p>es wurde eine Anfrage zum Zurücksetzen deines Passworts gestellt.</p>
            <div class="warning">
                <strong>Sicherheitshinweis:</strong> Diese Anfrage kam von IP {{request_ip}}
            </div>
            <p>Klicke auf den folgenden Link, um dein Passwort zurückzusetzen:</p>
            <a href="{{reset_url}}" class="reset-button">Passwort zurücksetzen</a>
            <p><strong>Reset-Code:</strong> {{reset_code}}</p>
            <p>Dieser Link ist {{expiry_hours}} Stunden gültig.</p>
            <p>&copy; {{current_year}} {{company_name}}</p>
        </div>
    </body>
    </html>
    HTML;

    file_put_contents($templatesDir . '/password-reset.html', $passwordResetTemplate);

    $this->templatesDir = $templatesDir;
});

afterEach(function () {
    // Cleanup test templates
    if (isset($this->templatesDir) && is_dir($this->templatesDir)) {
        array_map('unlink', glob($this->templatesDir . '/*'));
        rmdir($this->templatesDir);
    }
});

describe('WelcomeEmail', function () {
    it('creates a welcome email with proper validation', function () {
        $email = new WelcomeEmail(
            to: new Email('user@example.com'),
            userName: 'John Doe',
            companyName: 'Test Company',
            loginUrl: UrlFactory::parse('https://example.com/login'),
            referralCode: 'REF123'
        );

        expect($email->to->value)->toBe('user@example.com');
        expect($email->userName)->toBe('John Doe');
        expect($email->companyName)->toBe('Test Company');
        expect($email->loginUrl->toString())->toBe('https://example.com/login');
        expect($email->referralCode)->toBe('REF123');
        expect($email->priority)->toBe(Priority::HIGH);
    });

    it('validates required fields', function () {
        expect(function () {
            new WelcomeEmail(
                to: new Email('user@example.com'),
                userName: '',  // Empty name should fail
                companyName: 'Test Company',
                loginUrl: UrlFactory::parse('https://example.com/login')
            );
        })->toThrow(InvalidArgumentException::class);

        expect(function () {
            new WelcomeEmail(
                to: new Email('user@example.com'),
                userName: 'John Doe',
                companyName: '',  // Empty company should fail
                loginUrl: UrlFactory::parse('https://example.com/login')
            );
        })->toThrow(InvalidArgumentException::class);
    });

    it('converts to Mail Message correctly', function () {
        $parser = new DomTemplateParser($this->templatesDir);
        $cssInliner = new CssInliner($parser);
        $renderer = new EmailTemplateRenderer($parser, $cssInliner);

        $email = new WelcomeEmail(
            to: new Email('user@example.com'),
            userName: 'John Doe',
            companyName: 'Test Company',
            loginUrl: UrlFactory::parse('https://example.com/login'),
            referralCode: 'REF123'
        );

        $defaultFrom = new Email('noreply@company.com');
        $message = $email->toMessage($renderer, $defaultFrom);

        expect($message->from->value)->toBe('noreply@company.com');
        expect($message->to->toArray()[0]->value)->toBe('user@example.com');
        expect($message->subject)->toBe('Willkommen bei Test Company!');
        expect($message->priority)->toBe(Priority::HIGH);
        expect($message->htmlBody)->toContain('Hallo John Doe');
        expect($message->htmlBody)->toContain('Test Company');
        expect($message->htmlBody)->toContain('https://example.com/login');
        expect($message->htmlBody)->toContain('style='); // CSS should be inlined
        expect($message->body)->toContain('John Doe'); // Text version generated
    });

    it('uses factory method correctly', function () {
        $email = WelcomeEmail::for(
            to: 'user@example.com',
            userName: 'John Doe',
            companyName: 'Test Company',
            loginUrl: 'https://example.com/login',
            referralCode: 'REF123'
        );

        expect($email->to->value)->toBe('user@example.com');
        expect($email->loginUrl->toString())->toBe('https://example.com/login');
    });
});

describe('PasswordResetEmail', function () {
    it('creates a password reset email with proper validation', function () {
        $email = new PasswordResetEmail(
            to: new Email('user@example.com'),
            userName: 'John Doe',
            companyName: 'Test Company',
            resetUrl: UrlFactory::parse('https://example.com/reset?token=abc123'),
            resetCode: 'RESET-123-456',
            requestIp: '192.168.1.100',
            expiryHours: 24
        );

        expect($email->to->value)->toBe('user@example.com');
        expect($email->userName)->toBe('John Doe');
        expect($email->resetUrl->toString())->toBe('https://example.com/reset?token=abc123');
        expect($email->resetCode)->toBe('RESET-123-456');
        expect($email->requestIp)->toBe('192.168.1.100');
        expect($email->expiryHours)->toBe(24);
        expect($email->priority)->toBe(Priority::HIGHEST);
    });

    it('validates required fields', function () {
        expect(function () {
            new PasswordResetEmail(
                to: new Email('user@example.com'),
                userName: '',  // Empty name should fail
                companyName: 'Test Company',
                resetUrl: UrlFactory::parse('https://example.com/reset'),
                resetCode: 'RESET-123',
                requestIp: '192.168.1.100'
            );
        })->toThrow(InvalidArgumentException::class);

        expect(function () {
            new PasswordResetEmail(
                to: new Email('user@example.com'),
                userName: 'John Doe',
                companyName: 'Test Company',
                resetUrl: UrlFactory::parse('https://example.com/reset'),
                resetCode: '',  // Empty code should fail
                requestIp: '192.168.1.100'
            );
        })->toThrow(InvalidArgumentException::class);
    });

    it('validates expiry hours range', function () {
        expect(function () {
            new PasswordResetEmail(
                to: new Email('user@example.com'),
                userName: 'John Doe',
                companyName: 'Test Company',
                resetUrl: UrlFactory::parse('https://example.com/reset'),
                resetCode: 'RESET-123',
                requestIp: '192.168.1.100',
                expiryHours: 0  // Should fail
            );
        })->toThrow(InvalidArgumentException::class);

        expect(function () {
            new PasswordResetEmail(
                to: new Email('user@example.com'),
                userName: 'John Doe',
                companyName: 'Test Company',
                resetUrl: UrlFactory::parse('https://example.com/reset'),
                resetCode: 'RESET-123',
                requestIp: '192.168.1.100',
                expiryHours: 73  // Should fail (max 72)
            );
        })->toThrow(InvalidArgumentException::class);
    });

    it('converts to Mail Message correctly', function () {
        $parser = new DomTemplateParser($this->templatesDir);
        $cssInliner = new CssInliner($parser);
        $renderer = new EmailTemplateRenderer($parser, $cssInliner);

        $email = new PasswordResetEmail(
            to: new Email('user@example.com'),
            userName: 'John Doe',
            companyName: 'Test Company',
            resetUrl: UrlFactory::parse('https://example.com/reset?token=abc123'),
            resetCode: 'RESET-123-456',
            requestIp: '192.168.1.100',
            expiryHours: 24
        );

        $defaultFrom = new Email('security@company.com');
        $message = $email->toMessage($renderer, $defaultFrom);

        expect($message->from->value)->toBe('security@company.com');
        expect($message->to->toArray()[0]->value)->toBe('user@example.com');
        expect($message->subject)->toBe('Passwort zurücksetzen');
        expect($message->priority)->toBe(Priority::HIGHEST);
        expect($message->htmlBody)->toContain('Hallo John Doe');
        expect($message->htmlBody)->toContain('192.168.1.100');
        expect($message->htmlBody)->toContain('RESET-123-456');
        expect($message->htmlBody)->toContain('24 Stunden');
        expect($message->htmlBody)->toContain('style='); // CSS should be inlined
        expect($message->body)->toContain('John Doe'); // Text version generated
    });

    it('uses factory method correctly', function () {
        $email = PasswordResetEmail::for(
            to: 'user@example.com',
            userName: 'John Doe',
            companyName: 'Test Company',
            resetUrl: 'https://example.com/reset?token=abc123',
            resetCode: 'RESET-123-456',
            requestIp: '192.168.1.100',
            expiryHours: 48
        );

        expect($email->to->value)->toBe('user@example.com');
        expect($email->resetUrl->toString())->toBe('https://example.com/reset?token=abc123');
        expect($email->expiryHours)->toBe(48);
    });
});

describe('EmailService Integration', function () {
    it('sends typed emails via EmailService', function () {
        // Mock mailer that tracks sent messages
        $sentMessages = [];
        $mockMailer = new class ($sentMessages) implements MailerInterface {
            public function __construct(private array &$sentMessages)
            {
            }

            public function send(\App\Framework\Mail\Message $message): bool
            {
                $this->sentMessages[] = $message;

                return true;
            }

            public function queue(\App\Framework\Mail\Message $message, int $maxRetries = 3, int $delaySeconds = 0): bool
            {
                return true;
            }

            public function sendBatch(array $messages): array
            {
                return array_fill(0, count($messages), true);
            }
        };

        // Mock logger
        $logEntries = [];
        $mockLogger = new class ($logEntries) implements Logger {
            public function __construct(private array &$logEntries)
            {
            }

            public function debug(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'debug', 'message' => $message, 'context' => $context];
            }

            public function info(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'info', 'message' => $message, 'context' => $context];
            }

            public function notice(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'notice', 'message' => $message, 'context' => $context];
            }

            public function warning(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'warning', 'message' => $message, 'context' => $context];
            }

            public function error(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'error', 'message' => $message, 'context' => $context];
            }

            public function critical(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'critical', 'message' => $message, 'context' => $context];
            }

            public function alert(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'alert', 'message' => $message, 'context' => $context];
            }

            public function emergency(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'emergency', 'message' => $message, 'context' => $context];
            }
        };

        $parser = new DomTemplateParser($this->templatesDir);
        $cssInliner = new CssInliner($parser);

        $emailService = new EmailService(
            mailer: $mockMailer,
            parser: $parser,
            cssInliner: $cssInliner,
            logger: $mockLogger,
            defaultFrom: ['email' => 'noreply@company.com', 'name' => 'Test Company']
        );

        // Test WelcomeEmail
        $welcomeEmail = WelcomeEmail::for(
            to: 'newuser@example.com',
            userName: 'Jane Smith',
            companyName: 'Test Company',
            loginUrl: 'https://company.com/login'
        );

        $result = $emailService->send($welcomeEmail);

        expect($result)->toBeTrue();
        expect($sentMessages)->toHaveCount(1);

        $sentMessage = $sentMessages[0];
        expect($sentMessage->to->toArray()[0]->value)->toBe('newuser@example.com');
        expect($sentMessage->subject)->toBe('Willkommen bei Test Company!');
        expect($sentMessage->priority)->toBe(Priority::HIGH);
        expect($sentMessage->htmlBody)->toContain('Jane Smith');

        // Verify logging
        expect($logEntries)->toHaveCount(1);
        expect($logEntries[0]['level'])->toBe('info');
        expect($logEntries[0]['message'])->toBe('Typed email sent successfully');
        expect($logEntries[0]['context']['type'])->toBe(WelcomeEmail::class);

        // Test PasswordResetEmail
        $resetEmail = PasswordResetEmail::for(
            to: 'user@example.com',
            userName: 'John Doe',
            companyName: 'Test Company',
            resetUrl: 'https://company.com/reset?token=xyz789',
            resetCode: 'RESET-789',
            requestIp: '10.0.0.1',
            expiryHours: 12
        );

        $result2 = $emailService->send($resetEmail);

        expect($result2)->toBeTrue();
        expect($sentMessages)->toHaveCount(2);

        $sentMessage2 = $sentMessages[1];
        expect($sentMessage2->to->toArray()[0]->value)->toBe('user@example.com');
        expect($sentMessage2->subject)->toBe('Passwort zurücksetzen');
        expect($sentMessage2->priority)->toBe(Priority::HIGHEST);
        expect($sentMessage2->htmlBody)->toContain('RESET-789');
        expect($sentMessage2->htmlBody)->toContain('10.0.0.1');
        expect($sentMessage2->htmlBody)->toContain('12 Stunden');

        // Verify second logging entry
        expect($logEntries)->toHaveCount(2);
        expect($logEntries[1]['context']['type'])->toBe(PasswordResetEmail::class);
    });

    it('handles sending failures gracefully', function () {
        // Mock mailer that fails
        $mockMailer = new class () implements MailerInterface {
            public function send(\App\Framework\Mail\Message $message): bool
            {
                return false;
            }

            public function queue(\App\Framework\Mail\Message $message, int $maxRetries = 3, int $delaySeconds = 0): bool
            {
                return false;
            }

            public function sendBatch(array $messages): array
            {
                return array_fill(0, count($messages), false);
            }
        };

        $logEntries = [];
        $mockLogger = new class ($logEntries) implements Logger {
            public function __construct(private array &$logEntries)
            {
            }

            public function debug(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'debug', 'message' => $message, 'context' => $context];
            }

            public function info(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'info', 'message' => $message, 'context' => $context];
            }

            public function notice(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'notice', 'message' => $message, 'context' => $context];
            }

            public function warning(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'warning', 'message' => $message, 'context' => $context];
            }

            public function error(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'error', 'message' => $message, 'context' => $context];
            }

            public function critical(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'critical', 'message' => $message, 'context' => $context];
            }

            public function alert(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'alert', 'message' => $message, 'context' => $context];
            }

            public function emergency(string $message, array $context = []): void
            {
                $this->logEntries[] = ['level' => 'emergency', 'message' => $message, 'context' => $context];
            }
        };

        $parser = new DomTemplateParser($this->templatesDir);
        $cssInliner = new CssInliner($parser);

        $emailService = new EmailService(
            mailer: $mockMailer,
            parser: $parser,
            cssInliner: $cssInliner,
            logger: $mockLogger
        );

        $welcomeEmail = WelcomeEmail::for(
            to: 'user@example.com',
            userName: 'Test User',
            companyName: 'Test Company',
            loginUrl: 'https://company.com/login'
        );

        $result = $emailService->send($welcomeEmail);

        expect($result)->toBeFalse();
        expect($logEntries)->toHaveCount(1);
        expect($logEntries[0]['level'])->toBe('error');
        expect($logEntries[0]['message'])->toBe('Failed to send typed email');
        expect($logEntries[0]['context']['error'])->toBe('Send operation failed');
    });
});
