<?php

declare(strict_types=1);

namespace App\Framework\AsyncExamples\Mail;

use App\Framework\Async\AsyncQueue;
use App\Framework\Async\FiberManager;

/**
 * Asynchroner Mailer für Bulk-Email-Versand
 */
final class AsyncMailer
{
    /** @var array<string, mixed> */
    private array $config;

    private AsyncQueue $mailQueue;

    public function __construct(
        /** @var array<string, mixed> */
        array $config = [],
        private readonly FiberManager $fiberManager = new FiberManager()
    ) {
        $this->config = array_merge([
            'smtp_host' => 'localhost',
            'smtp_port' => 587,
            'smtp_username' => '',
            'smtp_password' => '',
            'smtp_encryption' => 'tls',
            'from_email' => 'noreply@example.com',
            'from_name' => 'Framework Mailer',
            'batch_size' => 10,
        ], $config);

        $this->mailQueue = new AsyncQueue(1000);
    }

    /**
     * Sendet eine einzelne E-Mail
     */
    public function send(Email $email): bool
    {
        try {
            return $this->sendEmail($email);
        } catch (\Throwable $e) {
            error_log("Failed to send email: " . $e->getMessage());

            return false;
        }
    }

    /**
     * Sendet mehrere E-Mails parallel
     *
     * @param array<int, Email> $emails
     * @return array<int, bool>
     */
    public function sendMultiple(array $emails): array
    {
        /** @var array<int, \Closure> */
        $operations = [];
        foreach ($emails as $key => $email) {
            $operations[$key] = fn () => $this->send($email);
        }

        return $this->fiberManager->batch($operations);
    }

    /**
     * Fügt E-Mails zur Queue hinzu
     */
    public function queue(Email $email): bool
    {
        return $this->mailQueue->enqueue($email);
    }

    /**
     * Fügt mehrere E-Mails zur Queue hinzu
     *
     * @param array<int, Email> $emails
     */
    public function queueMultiple(array $emails): int
    {
        $queued = 0;
        foreach ($emails as $email) {
            if ($this->queue($email)) {
                $queued++;
            }
        }

        return $queued;
    }

    /**
     * Startet den Queue-Worker
     */
    public function startWorker(): void
    {
        $this->fiberManager->async(function () {
            while (true) {
                $email = $this->mailQueue->dequeue();

                if ($email === null) {
                    break; // Queue geschlossen
                }

                if ($email instanceof Email) {
                    $this->send($email);
                }
            }
        });
    }

    /**
     * Verarbeitet Queue in Batches
     */
    public function processQueue(): int
    {
        $processed = 0;
        /** @var array<int, Email> */
        $batch = [];

        // Sammle Batch
        for ($i = 0; $i < $this->config['batch_size']; $i++) {
            $email = $this->mailQueue->tryDequeue();
            if ($email === null) {
                break;
            }
            $batch[] = $email;
        }

        if (! empty($batch)) {
            $results = $this->sendMultiple($batch);
            $processed = count(array_filter($results));
        }

        return $processed;
    }

    /**
     * Sendet Newsletter an mehrere Empfänger
     *
     * @param array<int, array<string, mixed>> $recipients
     * @return array<int, bool>
     */
    public function sendNewsletter(string $subject, string $content, array $recipients): array
    {
        /** @var array<int, Email> */
        $emails = [];
        foreach ($recipients as $key => $recipient) {
            $email = new Email(
                to: $recipient['email'],
                subject: $subject,
                body: $this->personalizeContent($content, $recipient),
                fromEmail: $this->config['from_email'],
                fromName: $this->config['from_name']
            );
            $emails[$key] = $email;
        }

        return $this->sendMultiple($emails);
    }

    private function sendEmail(Email $email): bool
    {
        // Vereinfachte SMTP-Implementation
        // In Produktion würde man eine echte SMTP-Library verwenden

        /** @var array<int, string> */
        $headers = [
            'From: ' . $email->fromName . ' <' . $email->fromEmail . '>',
            'Reply-To: ' . $email->fromEmail,
            'Content-Type: ' . ($email->isHtml ? 'text/html' : 'text/plain') . '; charset=UTF-8',
            'MIME-Version: 1.0',
        ];

        if (! empty($email->cc)) {
            $headers[] = 'Cc: ' . implode(', ', $email->cc);
        }

        if (! empty($email->bcc)) {
            $headers[] = 'Bcc: ' . implode(', ', $email->bcc);
        }

        $success = @mail(
            $email->to,
            $email->subject,
            $email->body,
            implode("\r\n", $headers)
        );

        return $success !== false;
    }

    /**
     * @param array<string, mixed> $recipient
     */
    private function personalizeContent(string $content, array $recipient): string
    {
        /** @var array<string, string> */
        $placeholders = [
            '{{name}}' => $recipient['name'] ?? '',
            '{{email}}' => $recipient['email'] ?? '',
            '{{first_name}}' => $recipient['first_name'] ?? '',
            '{{last_name}}' => $recipient['last_name'] ?? '',
        ];

        return str_replace(array_keys($placeholders), array_values($placeholders), $content);
    }

    /**
     * Stoppt den Mail-Queue-Worker
     */
    public function stopWorker(): void
    {
        $this->mailQueue->close();
    }

    /**
     * Gibt Mailer-Statistiken zurück
     *
     * @return array<string, mixed>
     */
    public function getStats(): array
    {
        return [
            'queue_stats' => $this->mailQueue->getStats(),
            'config' => [
                'batch_size' => $this->config['batch_size'],
                'smtp_host' => $this->config['smtp_host'],
            ],
        ];
    }
}
