<?php

namespace App\Console\Commands;

use App\Models\MailingBounce;
use App\Models\Mailing;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class ProcessBounceMailings extends Command
{
    /**
     * The name and signature of the console command.
     */
    protected $signature = 'mailings:process-bounces
                            {--mailbox= : Email address to check for bounces (e.g. bounces@yourdomain.com)}
                            {--host= : IMAP/POP3 server hostname}
                            {--port= : Server port (default: 993 for IMAP SSL, 995 for POP3 SSL)}
                            {--protocol= : Protocol to use: imap or pop3 (default: imap)}
                            {--username= : Login username}
                            {--password= : Login password}
                            {--ssl : Use SSL connection}
                            {--delete : Delete processed bounce emails}
                            {--dry-run : Show what would be processed without actually processing}';

    /**
     * The console command description.
     */
    protected $description = 'Process bounce emails from a dedicated bounce mailbox for mailings';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        // Get configuration from command options or config file
        $config = $this->getMailboxConfig();

        if (!$config) {
            $this->error('Mailbox configuration is required. Use options or configure in config/mail.php');
            return 1;
        }

        $dryRun = $this->option('dry-run');

        try {
            $connection = $this->connectToMailbox($config);
            $emails = $this->fetchBounceEmails($connection, $config);

            if (empty($emails)) {
                $this->info('No bounce emails found.');
                return 0;
            }

            $this->info("Found " . count($emails) . " bounce emails to process:");

            $processed = 0;
            foreach ($emails as $email) {
                $bounceInfo = $this->parseBounceEmail($email);

                if ($bounceInfo) {
                    $this->line("Processing bounce for: {$bounceInfo['email']} ({$bounceInfo['type']})");

                    if (!$dryRun) {
                        MailingBounce::recordBounce(
                            $bounceInfo['email'],
                            $bounceInfo['type'],
                            $bounceInfo['reason'],
                            $bounceInfo['mailing_id'] ?? null
                        );

                        if ($this->option('delete')) {
                            $this->deleteEmail($connection, $email['id'], $config);
                        }
                    }
                    $processed++;
                } else {
                    $this->warn("Could not parse bounce email ID: {$email['id']}");
                }
            }

            $this->info("Processed {$processed} bounce emails" . ($dryRun ? ' (dry run)' : ''));
            $this->closeConnection($connection, $config);

        } catch (\Exception $e) {
            $this->error("Error processing bounce emails: " . $e->getMessage());
            Log::error("Bounce processing error: " . $e->getMessage());
            return 1;
        }

        return 0;
    }

    /**
     * Get mailbox configuration
     */
    protected function getMailboxConfig(): ?array
    {
        // Try command options first
        if ($this->option('mailbox')) {
            return [
                'mailbox' => $this->option('mailbox'),
                'host' => $this->option('host'),
                'port' => $this->option('port') ?: ($this->option('protocol') === 'pop3' ? 995 : 993),
                'protocol' => $this->option('protocol') ?: 'imap',
                'username' => $this->option('username'),
                'password' => $this->option('password'),
                'ssl' => $this->option('ssl')
            ];
        }

        // Try config file
        $config = config('mail.bounce_processing');
        if ($config && isset($config['mailbox'])) {
            return $config;
        }

        return null;
    }

    /**
     * Connect to mailbox
     */
    protected function connectToMailbox(array $config)
    {
        $protocol = strtolower($config['protocol']);

        if ($protocol === 'imap') {
            return $this->connectIMAP($config);
        } elseif ($protocol === 'pop3') {
            return $this->connectPOP3($config);
        }

        throw new \Exception("Unsupported protocol: {$protocol}");
    }

    /**
     * Connect via IMAP
     */
    protected function connectIMAP(array $config)
    {
        $host = $config['host'];
        $port = $config['port'];
        $ssl = $config['ssl'] ? '/ssl' : '';

        $mailbox = "{{$host}:{$port}/imap{$ssl}}INBOX";

        $connection = imap_open($mailbox, $config['username'], $config['password']);

        if (!$connection) {
            throw new \Exception("Failed to connect to IMAP server: " . imap_last_error());
        }

        return $connection;
    }

    /**
     * Connect via POP3 (basic implementation)
     */
    protected function connectPOP3(array $config)
    {
        throw new \Exception("POP3 support not implemented yet. Use IMAP instead.");
    }

    /**
     * Fetch bounce emails
     */
    protected function fetchBounceEmails($connection, array $config): array
    {
        $emails = [];
        $numMessages = imap_num_msg($connection);

        for ($i = 1; $i <= $numMessages; $i++) {
            $header = imap_headerinfo($connection, $i);
            $subject = $header->subject ?? '';

            // Check if this looks like a bounce email
            if ($this->isBounceEmail($subject, $header)) {
                $body = imap_body($connection, $i);
                $emails[] = [
                    'id' => $i,
                    'subject' => $subject,
                    'body' => $body,
                    'header' => $header
                ];
            }
        }

        return $emails;
    }

    /**
     * Check if email is a bounce
     */
    protected function isBounceEmail(string $subject, $header): bool
    {
        $bounceIndicators = [
            'delivery status notification',
            'returned mail',
            'undelivered mail',
            'mail delivery failed',
            'bounce',
            'mailer-daemon',
            'postmaster',
            'delivery failure',
            'mail system error'
        ];

        $subjectLower = strtolower($subject);
        foreach ($bounceIndicators as $indicator) {
            if (strpos($subjectLower, $indicator) !== false) {
                return true;
            }
        }

        // Check sender
        $from = $header->from[0]->mailbox ?? '';
        $bounceFroms = ['mailer-daemon', 'postmaster', 'mail-daemon'];

        foreach ($bounceFroms as $bounceSender) {
            if (strpos(strtolower($from), $bounceSender) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Parse bounce email to extract information
     */
    protected function parseBounceEmail(array $email): ?array
    {
        $body = $email['body'];
        $subject = $email['subject'];

        // Extract original recipient email
        $recipientEmail = $this->extractRecipientEmail($body);
        if (!$recipientEmail) {
            return null;
        }

        // Determine bounce type and reason
        $bounceType = $this->determineBounceType($body, $subject);
        $reason = $this->extractBounceReason($body, $subject);

        // Try to extract mailing ID if present
        $mailingId = $this->extractMailingIdFromBounce($body, $subject);

        return [
            'email' => $recipientEmail,
            'type' => $bounceType,
            'reason' => $reason,
            'mailing_id' => $mailingId
        ];
    }

    /**
     * Extract recipient email from bounce message
     */
    protected function extractRecipientEmail(string $body): ?string
    {
        // Common patterns for recipient extraction
        $patterns = [
            '/(?:to|for|recipient):\s*<?([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>?/i',
            '/final-recipient:\s*rfc822;\s*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i',
            '/original-recipient:\s*rfc822;\s*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i',
            '/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i' // Generic email pattern
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $body, $matches)) {
                return $matches[1];
            }
        }

        return null;
    }

    /**
     * Determine bounce type from message content
     */
    protected function determineBounceType(string $body, string $subject): string
    {
        $bodyLower = strtolower($body . ' ' . $subject);

        // Hard bounce patterns
        $hardBouncePatterns = [
            'user unknown',
            'no such user',
            'invalid recipient',
            'recipient address rejected',
            'mailbox unavailable',
            'does not exist',
            '5.1.1', '5.1.2', '5.1.3', // SMTP codes
            '550', '551', '553', '554'
        ];

        foreach ($hardBouncePatterns as $pattern) {
            if (strpos($bodyLower, $pattern) !== false) {
                return 'hard';
            }
        }

        // Soft bounce patterns
        $softBouncePatterns = [
            'mailbox full',
            'quota exceeded',
            'temporarily rejected',
            'try again later',
            'temporarily unavailable',
            '4.2.2', '4.3.1', '4.3.2', // SMTP codes
            '421', '450', '451', '452'
        ];

        foreach ($softBouncePatterns as $pattern) {
            if (strpos($bodyLower, $pattern) !== false) {
                return 'soft';
            }
        }

        return 'unknown';
    }

    /**
     * Extract bounce reason
     */
    protected function extractBounceReason(string $body, string $subject): string
    {
        // Look for diagnostic code or action field
        if (preg_match('/diagnostic-code:\s*(.+)/i', $body, $matches)) {
            return trim($matches[1]);
        }

        if (preg_match('/action:\s*(.+)/i', $body, $matches)) {
            return trim($matches[1]);
        }

        // Fallback to subject
        return substr($subject, 0, 255);
    }

    /**
     * Extract mailing ID from bounce if present
     */
    protected function extractMailingIdFromBounce(string $body, string $subject): ?int
    {
        // Look for custom headers or message IDs that contain mailing info
        if (preg_match('/mailing[_-]?id[:\s]*(\d+)/i', $body, $matches)) {
            return (int) $matches[1];
        }

        if (preg_match('/x-mailing-id[:\s]*(\d+)/i', $body, $matches)) {
            return (int) $matches[1];
        }

        return null;
    }

    /**
     * Delete processed email
     */
    protected function deleteEmail($connection, int $messageId, array $config): void
    {
        if ($config['protocol'] === 'imap') {
            imap_delete($connection, $messageId);
            imap_expunge($connection);
        }
    }

    /**
     * Close connection
     */
    protected function closeConnection($connection, array $config): void
    {
        if ($config['protocol'] === 'imap') {
            imap_close($connection);
        }
    }
}