<?php

namespace App\Services;

use App\Models\EmailLog;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Exception;

/**
 * EmailProvider
 * 
 * Handles direct communication with email servers (SMTP/IMAP).
 * Provides low-level email operations with comprehensive error handling.
 * 
 * Features:
 * - SMTP email sending
 * - IMAP email receiving
 * - Connection testing
 * - Email validation
 * - Operation logging
 * 
 * @package App\Services
 */
class EmailProvider
{
    /**
     * Configuration service instance.
     *
     * @var EmailConfigurationService
     */
    protected EmailConfigurationService $configService;

    /**
     * Create a new EmailProvider instance.
     *
     * @param EmailConfigurationService $configService
     */
    public function __construct(EmailConfigurationService $configService)
    {
        $this->configService = $configService;
    }

    /**
     * Send an email via SMTP.
     *
     * @param string $to Recipient email address
     * @param string $subject Email subject
     * @param string $body Email body content
     * @param bool $isHtml Whether body is HTML (default: false)
     * @return array Result with success status and details
     */
    public function sendEmail(string $to, string $subject, string $body, bool $isHtml = false): array
    {
        $startTime = microtime(true);
        $config = $this->configService->getSmtpConfig();

        if (!$config) {
            return [
                'success' => false,
                'message' => 'SMTP is not configured',
                'error' => 'No active SMTP configuration found',
            ];
        }

        // Validate recipient email
        if (!$this->validateEmailAddress($to)) {
            return [
                'success' => false,
                'message' => 'Invalid recipient email address',
                'error' => "Invalid email: {$to}",
            ];
        }

        try {
            // Configure mailer dynamically
            $this->configureMailer($config);

            // Build the email
            $fromAddress = $config['from']['address'] ?? $config['username'] ?? config('mail.from.address');
            $fromName = $config['from']['name'] ?? config('app.name');

            // Send using Laravel Mail
            Mail::send([], [], function ($message) use ($to, $subject, $body, $isHtml, $fromAddress, $fromName) {
                $message->to($to)
                    ->from($fromAddress, $fromName)
                    ->subject($subject);

                if ($isHtml) {
                    $message->html($body);
                } else {
                    $message->text($body);
                }
            });

            $executionTime = round((microtime(true) - $startTime) * 1000);
            $messageId = Str::random(32);

            // Log successful send
            EmailLog::logSmtpSend(
                $to,
                $subject,
                true,
                ['message_id' => $messageId, 'mailer' => 'smtp'],
                null,
                (int) $executionTime
            );

            return [
                'success' => true,
                'messageId' => $messageId,
                'message' => 'Email sent successfully',
                'execution_time_ms' => $executionTime,
            ];

        } catch (Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000);

            // Log failed send
            EmailLog::logSmtpSend(
                $to,
                $subject,
                false,
                null,
                $e->getMessage(),
                (int) $executionTime
            );

            Log::error('SMTP send failed', [
                'to' => $to,
                'subject' => $subject,
                'error' => $e->getMessage(),
            ]);

            return [
                'success' => false,
                'message' => 'Failed to send email',
                'error' => $e->getMessage(),
                'execution_time_ms' => $executionTime,
            ];
        }
    }

    /**
     * Receive emails from IMAP inbox.
     *
     * @param int $limit Maximum number of emails to fetch (default: 10)
     * @return array Result with success status and emails data
     */
    public function receiveEmails(int $limit = 10): array
    {
        $startTime = microtime(true);
        $config = $this->configService->getImapConfig();

        if (!$config) {
            return [
                'success' => false,
                'message' => 'IMAP is not configured',
                'error' => 'No active IMAP configuration found',
            ];
        }

        try {
            // Build IMAP connection string
            $encryption = $config['encryption'] === 'ssl' ? '/ssl' : '';
            $mailbox = "{{$config['host']}:{$config['port']}/imap{$encryption}}INBOX";

            // Connect to IMAP server
            $imap = @imap_open($mailbox, $config['username'], $config['password']);

            if (!$imap) {
                throw new Exception(imap_last_error() ?: 'Failed to connect to IMAP server');
            }

            // Get emails
            $emails = [];
            $emailIds = imap_search($imap, 'ALL', SE_UID);

            if ($emailIds) {
                // Get the most recent emails
                $emailIds = array_slice(array_reverse($emailIds), 0, $limit);

                foreach ($emailIds as $emailId) {
                    $header = imap_headerinfo($imap, imap_msgno($imap, $emailId));
                    $structure = imap_fetchstructure($imap, $emailId, FT_UID);

                    $email = [
                        'id' => $emailId,
                        'subject' => isset($header->subject) ? imap_utf8($header->subject) : '(No Subject)',
                        'from' => isset($header->from[0]) ? [
                            'email' => $header->from[0]->mailbox . '@' . $header->from[0]->host,
                            'name' => isset($header->from[0]->personal) ? imap_utf8($header->from[0]->personal) : null,
                        ] : null,
                        'to' => isset($header->to[0]) ? $header->to[0]->mailbox . '@' . $header->to[0]->host : null,
                        'date' => isset($header->date) ? date('Y-m-d H:i:s', strtotime($header->date)) : null,
                        'size' => $structure->bytes ?? 0,
                        'unread' => !($header->Unseen ?? ' ') === ' ',
                        'has_attachments' => isset($structure->parts) && count($structure->parts) > 1,
                    ];

                    $emails[] = $email;
                }
            }

            imap_close($imap);

            $executionTime = round((microtime(true) - $startTime) * 1000);

            // Log successful receive
            EmailLog::logImapReceive(
                true,
                ['count' => count($emails), 'limit' => $limit],
                null,
                (int) $executionTime
            );

            return [
                'success' => true,
                'message' => 'Emails retrieved successfully',
                'count' => count($emails),
                'emails' => $emails,
                'execution_time_ms' => $executionTime,
            ];

        } catch (Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000);

            // Log failed receive
            EmailLog::logImapReceive(
                false,
                null,
                $e->getMessage(),
                (int) $executionTime
            );

            Log::error('IMAP receive failed', [
                'error' => $e->getMessage(),
            ]);

            return [
                'success' => false,
                'message' => 'Failed to retrieve emails',
                'error' => $e->getMessage(),
                'execution_time_ms' => $executionTime,
            ];
        }
    }

    /**
     * Test SMTP connection.
     *
     * @return array Result with success status and details
     */
    public function testSmtpConnection(): array
    {
        $startTime = microtime(true);
        $config = $this->configService->getSmtpConfig();

        if (!$config) {
            return [
                'success' => false,
                'message' => 'SMTP is not configured',
                'error' => 'No active SMTP configuration found',
            ];
        }

        try {
            // Attempt connection using PHP's fsockopen
            $encryption = $config['encryption'];
            $host = $config['host'];
            $port = $config['port'];
            $timeout = 10;

            // Build connection URL
            $protocol = $encryption === 'ssl' ? 'ssl://' : '';
            $socket = @fsockopen($protocol . $host, $port, $errno, $errstr, $timeout);

            if (!$socket) {
                throw new Exception("Connection failed: {$errstr} ({$errno})");
            }

            // Read initial response
            $response = fgets($socket);

            // Send EHLO
            fwrite($socket, "EHLO localhost\r\n");
            $ehloResponse = '';
            while ($line = fgets($socket)) {
                $ehloResponse .= $line;
                if (substr($line, 3, 1) === ' ') break;
            }

            fclose($socket);

            $executionTime = round((microtime(true) - $startTime) * 1000);

            // Log successful test
            EmailLog::logTest(
                'smtp',
                true,
                [
                    'host' => $host,
                    'port' => $port,
                    'encryption' => $encryption,
                    'initial_response' => trim($response),
                ],
                null,
                (int) $executionTime
            );

            return [
                'success' => true,
                'message' => 'SMTP connection successful',
                'details' => [
                    'host' => $host,
                    'port' => $port,
                    'encryption' => $encryption,
                    'response' => trim($response),
                ],
                'execution_time_ms' => $executionTime,
            ];

        } catch (Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000);

            // Log failed test
            EmailLog::logTest(
                'smtp',
                false,
                null,
                $e->getMessage(),
                (int) $executionTime
            );

            return [
                'success' => false,
                'message' => 'SMTP connection failed',
                'error' => $e->getMessage(),
                'execution_time_ms' => $executionTime,
            ];
        }
    }

    /**
     * Test IMAP connection.
     *
     * @return array Result with success status and details
     */
    public function testImapConnection(): array
    {
        $startTime = microtime(true);
        $config = $this->configService->getImapConfig();

        if (!$config) {
            return [
                'success' => false,
                'message' => 'IMAP is not configured',
                'error' => 'No active IMAP configuration found',
            ];
        }

        try {
            // Build IMAP connection string
            $encryption = $config['encryption'];
            $host = $config['host'];
            $port = $config['port'];
            $username = $config['username'];
            $password = $config['password'];

            $encryptionFlag = $encryption === 'ssl' ? '/ssl' : '';
            $mailbox = "{{$host}:{$port}/imap{$encryptionFlag}}INBOX";

            // Attempt connection
            $imap = @imap_open($mailbox, $username, $password, OP_READONLY, 1);

            if (!$imap) {
                $error = imap_last_error() ?: 'Connection failed';
                throw new Exception($error);
            }

            // Get mailbox info
            $check = imap_check($imap);
            $status = imap_status($imap, $mailbox, SA_MESSAGES | SA_UNSEEN);

            imap_close($imap);

            $executionTime = round((microtime(true) - $startTime) * 1000);

            // Log successful test
            EmailLog::logTest(
                'imap',
                true,
                [
                    'host' => $host,
                    'port' => $port,
                    'encryption' => $encryption,
                    'messages' => $status->messages ?? 0,
                    'unseen' => $status->unseen ?? 0,
                ],
                null,
                (int) $executionTime
            );

            return [
                'success' => true,
                'message' => 'IMAP connection successful',
                'details' => [
                    'host' => $host,
                    'port' => $port,
                    'encryption' => $encryption,
                    'total_messages' => $status->messages ?? 0,
                    'unread_messages' => $status->unseen ?? 0,
                ],
                'execution_time_ms' => $executionTime,
            ];

        } catch (Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000);

            // Log failed test
            EmailLog::logTest(
                'imap',
                false,
                null,
                $e->getMessage(),
                (int) $executionTime
            );

            return [
                'success' => false,
                'message' => 'IMAP connection failed',
                'error' => $e->getMessage(),
                'execution_time_ms' => $executionTime,
            ];
        }
    }

    /**
     * Validate an email address format.
     *
     * @param string $email Email address to validate
     * @return bool True if valid, false otherwise
     */
    public function validateEmailAddress(string $email): bool
    {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }

    /**
     * Configure the mailer dynamically based on config.
     *
     * Symfony Mailer transport scheme mapping:
     * - 'ssl' (port 465) → transport 'smtps', encryption null (SSL is implicit)
     * - 'tls' (port 587) → transport 'smtp', encryption 'tls'
     * - 'none' (port 25)  → transport 'smtp', encryption null
     *
     * @param array $config SMTP configuration
     * @return void
     */
    protected function configureMailer(array $config): void
    {
        // Purge any cached mailer instance so the new config takes effect immediately
        Mail::purge('smtp');

        $encryption = strtolower($config['encryption'] ?? 'tls');

        // Symfony Mailer does not accept 'ssl' as an encryption value.
        // Port 465 SSL must use the 'smtps' transport with no explicit encryption.
        // Port 587 TLS uses the 'smtp' transport with encryption = 'tls'.
        if ($encryption === 'ssl') {
            $transport   = 'smtps';
            $encryptVal  = null;
        } elseif ($encryption === 'tls') {
            $transport   = 'smtp';
            $encryptVal  = 'tls';
        } else {
            // 'none' or anything else
            $transport   = 'smtp';
            $encryptVal  = null;
        }

        config([
            'mail.mailers.smtp' => [
                'transport'  => $transport,
                'host'       => $config['host'],
                'port'       => $config['port'],
                'encryption' => $encryptVal,
                'username'   => $config['username'],
                'password'   => $config['password'],
                'timeout'    => 30,
            ],
            'mail.from' => [
                'address' => $config['from']['address'] ?? $config['username'],
                'name'    => $config['from']['name'] ?? config('app.name'),
            ],
        ]);

        config(['mail.default' => 'smtp']);
    }

    /**
     * Get supported encryption types.
     *
     * @return array
     */
    public static function getEncryptionTypes(): array
    {
        return [
            'tls' => 'TLS',
            'ssl' => 'SSL',
            'none' => 'None',
        ];
    }

    /**
     * Get common SMTP port recommendations.
     *
     * @return array
     */
    public static function getCommonSmtpPorts(): array
    {
        return [
            25 => 'Standard SMTP',
            465 => 'SMTP over SSL',
            587 => 'SMTP with TLS (Recommended)',
            2525 => 'Alternative SMTP',
        ];
    }

    /**
     * Get common IMAP port recommendations.
     *
     * @return array
     */
    public static function getCommonImapPorts(): array
    {
        return [
            143 => 'Standard IMAP',
            993 => 'IMAP over SSL (Recommended)',
        ];
    }
}
