<?php

namespace App\Services;

use App\Models\EmailLog;
use App\Models\MailProfile;
use Symfony\Component\Mailer\Mailer as SymfonyMailer;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;

class EmailSender
{
    public function __construct(public EmailTemplateRenderer $renderer) {}

    /**
     * Build and send the message described by the EmailLog. Returns ['message_id' => string|null].
     * Throws on transport errors so the Job can retry.
     */
    public function sendFromLog(EmailLog $log): array
    {
        // Resolve sending profile
        $profile = null;
        if ($log->mail_profile_id) {
            $profile = MailProfile::query()->find($log->mail_profile_id);
        }
        if (! $profile) {
            $profile = MailProfile::query()->where('active', true)->orderBy('priority')->orderBy('id')->first();
        }

        $embed = $log->embed_mode ?: 'base64';

        $subject = (string) $log->subject;
        $html = (string) optional($log->body)->body_html ?? '';
        $text = (string) optional($log->body)->body_text ?? '';

        // Inline CSS and handle images similarly to controller
        if ($html !== '') {
            if ($embed === 'base64') {
                try {
                    $imageInliner = app(\App\Services\EmailImageInliner::class);
                    $html = $imageInliner->inline($html);
                } catch (\Throwable $e) {
                }
            } else {
                // Best effort absolutize /storage URLs using app.url
                $base = (string) (config('app.asset_url') ?: config('app.url'));
                $host = $base !== '' ? rtrim($base, '/') : null;
                if ($host) {
                    $html = preg_replace_callback('#<img([^>]+)src=["\']([^"\']+)["\']([^>]*)>#i', function (array $m) use ($host) {
                        $before = $m[1] ?? '';
                        $src = $m[2] ?? '';
                        $after = $m[3] ?? '';
                        $path = $src;
                        if (preg_match('#^https?://#i', $src)) {
                            $parts = parse_url($src);
                            $path = $parts['path'] ?? '';
                            if (! preg_match('#^/?storage/#i', (string) $path)) {
                                return $m[0];
                            }
                        } else {
                            if (! preg_match('#^/?storage/#i', (string) $path)) {
                                return $m[0];
                            }
                        }
                        $rel = '/'.ltrim(preg_replace('#^/?storage/#i', 'storage/', (string) $path), '/');
                        $abs = rtrim($host, '/').$rel;

                        return '<img'.$before.'src="'.$abs.'"'.$after.'>';
                    }, $html);
                }
            }
            try {
                $inliner = new CssToInlineStyles;
                $html = $inliner->convert($html);
            } catch (\Throwable $e) {
            }
        }

        // Transport setup (Symfony Mailer preferred when profile exists)
        $messageId = null;
        if ($profile) {
            $host = $profile->host;
            $port = (int) ($profile->port ?: 587);
            $encryption = $profile->encryption ?: 'tls';
            $username = $profile->username ?: '';
            $password = (string) ($profile->decryptPassword() ?? '');

            $scheme = $encryption === 'ssl' ? 'smtps' : 'smtp';
            $query = $encryption === 'tls' ? '?encryption=tls' : '';
            $dsn = sprintf('%s://%s:%s@%s:%d%s', $scheme, rawurlencode($username), rawurlencode($password), $host, $port, $query);

            $transport = Transport::fromDsn($dsn);
            $mailer = new SymfonyMailer($transport);

            // Resolve a valid From address without falling back to to_email
            $fromAddrCandidates = [
                (string) ($log->from_email ?? ''),
                (string) ($profile->from_address ?? ''),
                (string) ($username ?? ''),
                (string) (config('mail.from.address') ?? ''),
            ];
            $fromAddr = '';
            foreach ($fromAddrCandidates as $cand) {
                $cand = trim($cand);
                if ($cand !== '' && filter_var($cand, FILTER_VALIDATE_EMAIL)) {
                    $fromAddr = $cand;
                    break;
                }
            }
            $fromName = (string) ($log->from_name ?: ($profile->from_name ?: (config('mail.from.name') ?? config('app.name') ?? '')));

            // Fallback From if still empty: use a no-reply at app host
            if ($fromAddr === '') {
                $appUrl = (string) (config('app.url') ?? '');
                $hostPart = '';
                if ($appUrl !== '') {
                    $parsed = @parse_url($appUrl);
                    $hostPart = is_array($parsed) ? (string) ($parsed['host'] ?? '') : '';
                }
                $domain = $hostPart !== '' ? $hostPart : 'localhost.localdomain';
                $fromAddr = 'no-reply@'.$domain;
            }

            // Build email with safe Address instances (Symfony Address does not allow null name)
            $fromAddress = $fromName !== ''
                ? new Address($fromAddr, $fromName)
                : new Address($fromAddr);

            $email = (new Email)
                ->from($fromAddress)
                ->subject($subject);

            // Address recipients: prefer multi-recipient list; fall back to validated single address
            $toList = (array) ($log->to_recipients ?? []);
            $addresses = [];
            foreach ($toList as $addr) {
                $addr = trim((string) $addr);
                if ($addr !== '' && filter_var($addr, FILTER_VALIDATE_EMAIL)) {
                    $addresses[] = new Address($addr);
                }
            }
            if (! empty($addresses)) {
                $email->to(...$addresses);
            } else {
                // Validate single to_email before using
                $singleTo = trim((string) $log->to_email);
                if ($singleTo === '' || ! filter_var($singleTo, FILTER_VALIDATE_EMAIL)) {
                    throw new \InvalidArgumentException('No valid recipient email found for EmailLog #'.$log->id);
                }
                $email->to(new Address($singleTo, (string) ($log->to_name ?? '')));
            }

            if (! empty($text)) {
                $email->text($text);
            }
            if (! empty($html)) {
                $email->html($html);
            }
            if (! empty($log->reply_to)) {
                $email->replyTo($log->reply_to);
            }

            // Attach files if present on the log
            $attachments = (array) ($log->attachments ?? []);
            foreach ($attachments as $att) {
                try {
                    $disk = $att['disk'] ?? 'public';
                    $rawPath = $att['path'] ?? null;
                    if (! $rawPath) {
                        continue;
                    }
                    $name = $att['name'] ?? basename($rawPath);
                    $mime = $att['mime'] ?? 'application/octet-stream';

                    $storage = \Storage::disk($disk);
                    // Build candidate paths (normalize slashes and strip legacy prefixes)
                    $norm = ltrim(str_replace('\\', '/', (string) $rawPath), '/');
                    $candidates = [];
                    $candidates[] = $norm;
                    if (str_starts_with($norm, 'public/')) {
                        $candidates[] = substr($norm, 7);
                    }
                    if (str_starts_with($norm, 'storage/')) {
                        $candidates[] = substr($norm, 8);
                    }
                    $candidates = array_values(array_unique(array_filter($candidates)));

                    // Diagnostics (guarded by app.debug)
                    if (config('app.debug')) {
                        try {
                            \Log::debug('EmailSender: attachment diagnostics (Symfony mailer)', [
                                'log_id' => $log->id,
                                'disk' => $disk,
                                'disk_driver' => config('filesystems.disks.'.$disk.'.driver'),
                                'raw_path' => $rawPath,
                                'normalized' => $norm,
                                'candidates' => $candidates,
                            ]);
                        } catch (\Throwable $e) {
                            // ignore logging failures
                        }
                    }

                    $attached = false;
                    // Try local filesystem path first
                    foreach ($candidates as $cand) {
                        try {
                            if (method_exists($storage, 'path')) {
                                $full = $storage->path($cand);
                                if (is_string($full) && is_file($full)) {
                                    if (config('app.debug')) {
                                        try {
                                            \Log::debug('EmailSender: attaching from local path', [
                                                'log_id' => $log->id,
                                                'disk' => $disk,
                                                'candidate' => $cand,
                                                'full_path' => $full,
                                                'filesize' => @filesize($full) ?: null,
                                                'mime' => $mime,
                                                'name' => $name,
                                            ]);
                                        } catch (\Throwable $e) {
                                        }
                                    }
                                    $email->attachFromPath($full, $name, $mime);
                                    $attached = true;
                                    break;
                                }
                            }
                        } catch (\Throwable $e) {
                        }
                    }

                    if (! $attached) {
                        // Fallback for non-local disks (S3, etc.): read bytes and attach
                        foreach ($candidates as $cand) {
                            try {
                                $exists = $storage->exists($cand);
                                if (config('app.debug')) {
                                    try {
                                        \Log::debug('EmailSender: storage exists check', [
                                            'log_id' => $log->id,
                                            'disk' => $disk,
                                            'candidate' => $cand,
                                            'exists' => $exists,
                                        ]);
                                    } catch (\Throwable $e) {
                                    }
                                }
                                if ($exists) {
                                    $bytes = $storage->get($cand);
                                    if (! is_null($bytes) && $bytes !== false) {
                                        if (config('app.debug')) {
                                            try {
                                                \Log::debug('EmailSender: attaching from bytes (Symfony)', [
                                                    'log_id' => $log->id,
                                                    'disk' => $disk,
                                                    'candidate' => $cand,
                                                    'byte_length' => is_string($bytes) ? strlen($bytes) : null,
                                                    'mime' => $mime,
                                                    'name' => $name,
                                                ]);
                                            } catch (\Throwable $e) {
                                            }
                                        }
                                        $email->attach($bytes, $name, $mime);
                                        $attached = true;
                                        break;
                                    }
                                }
                            } catch (\Throwable $e) {
                            }
                        }
                    }

                    if (config('app.debug')) {
                        try {
                            \Log::debug('EmailSender: attachment result (Symfony mailer)', [
                                'log_id' => $log->id,
                                'name' => $name,
                                'attached' => $attached,
                            ]);
                        } catch (\Throwable $e) {
                        }
                    }
                } catch (\Throwable $e) {
                    // ignore individual attachment failures; continue sending
                }
            }

            $mailer->send($email);
            $headers = $email->getHeaders();
            $messageIdHeader = $headers->get('Message-ID');
            $messageId = $messageIdHeader ? $messageIdHeader->getBodyAsString() : null;
        } else {
            // Fallback to Laravel mailer
            if (! empty($html)) {
                \Mail::html($html, function ($message) use ($log, $subject, $text) {
                    $toList = (array) ($log->to_recipients ?? []);
                    if (! empty($toList)) {
                        $message->to($toList);
                    } else {
                        $singleTo = trim((string) $log->to_email);
                        if ($singleTo === '' || ! filter_var($singleTo, FILTER_VALIDATE_EMAIL)) {
                            throw new \InvalidArgumentException('No valid recipient email found for EmailLog #'.$log->id);
                        }
                        $toName = (string) ($log->to_name ?? '');
                        if ($toName !== '') {
                            $message->to($singleTo, $toName);
                        } else {
                            $message->to($singleTo);
                        }
                    }
                    $message->subject($subject);
                    if (! empty($log->reply_to)) {
                        $message->replyTo($log->reply_to);
                    }
                    if (! empty($text)) {
                        // Provide a plain text alternative when available
                        $message->text($text);
                    }

                    // Attachments via Laravel Mailer (supports storage disks)
                    $attachments = (array) ($log->attachments ?? []);
                    foreach ($attachments as $att) {
                        try {
                            $disk = $att['disk'] ?? 'public';
                            $rawPath = $att['path'] ?? null;
                            if (! $rawPath) {
                                continue;
                            }
                            $name = $att['name'] ?? basename($rawPath);
                            $mime = $att['mime'] ?? 'application/octet-stream';

                            $norm = ltrim(str_replace('\\', '/', (string) $rawPath), '/');
                            $candidates = [$norm];
                            if (str_starts_with($norm, 'public/')) {
                                $candidates[] = substr($norm, 7);
                            }
                            if (str_starts_with($norm, 'storage/')) {
                                $candidates[] = substr($norm, 8);
                            }
                            if (config('app.debug')) {
                                try {
                                    \Log::debug('EmailSender: attachment diagnostics (Laravel html)', [
                                        'log_id' => $log->id,
                                        'disk' => $disk,
                                        'disk_driver' => config('filesystems.disks.'.$disk.'.driver'),
                                        'raw_path' => $rawPath,
                                        'normalized' => $norm,
                                        'candidates' => array_values(array_unique($candidates)),
                                    ]);
                                } catch (\Throwable $e) {
                                }
                            }
                            $attached = false;
                            foreach (array_values(array_unique($candidates)) as $cand) {
                                try {
                                    $message->attachFromStorageDisk($disk, $cand, $name, ['mime' => $mime]);
                                    if (config('app.debug')) {
                                        try {
                                            \Log::debug('EmailSender: attached via attachFromStorageDisk (Laravel html)', [
                                                'log_id' => $log->id,
                                                'disk' => $disk,
                                                'candidate' => $cand,
                                                'mime' => $mime,
                                                'name' => $name,
                                            ]);
                                        } catch (\Throwable $e) {
                                        }
                                    }
                                    $attached = true;
                                    break;
                                } catch (\Throwable $e) {
                                    // fall back to bytes
                                }
                            }
                            if (! $attached) {
                                $storage = \Storage::disk($disk);
                                foreach (array_values(array_unique($candidates)) as $cand) {
                                    try {
                                        $exists = $storage->exists($cand);
                                        if (config('app.debug')) {
                                            try {
                                                \Log::debug('EmailSender: storage exists check (Laravel html)', [
                                                    'log_id' => $log->id,
                                                    'disk' => $disk,
                                                    'candidate' => $cand,
                                                    'exists' => $exists,
                                                ]);
                                            } catch (\Throwable $e) {
                                            }
                                        }
                                        if ($exists) {
                                            $bytes = $storage->get($cand);
                                            if (! is_null($bytes) && $bytes !== false) {
                                                if (config('app.debug')) {
                                                    try {
                                                        \Log::debug('EmailSender: attaching from bytes (Laravel html)', [
                                                            'log_id' => $log->id,
                                                            'disk' => $disk,
                                                            'candidate' => $cand,
                                                            'byte_length' => is_string($bytes) ? strlen($bytes) : null,
                                                            'mime' => $mime,
                                                            'name' => $name,
                                                        ]);
                                                    } catch (\Throwable $e) {
                                                    }
                                                }
                                                $message->attachData($bytes, $name, ['mime' => $mime]);
                                                break;
                                            }
                                        }
                                    } catch (\Throwable $e) {
                                    }
                                }
                            }
                        } catch (\Throwable $e) {
                        }
                    }
                });
            } else {
                \Mail::raw($text ?: '', function ($message) use ($log, $subject) {
                    $toList = (array) ($log->to_recipients ?? []);
                    if (! empty($toList)) {
                        $message->to($toList);
                    } else {
                        $singleTo = trim((string) $log->to_email);
                        if ($singleTo === '' || ! filter_var($singleTo, FILTER_VALIDATE_EMAIL)) {
                            throw new \InvalidArgumentException('No valid recipient email found for EmailLog #'.$log->id);
                        }
                        $toName = (string) ($log->to_name ?? '');
                        if ($toName !== '') {
                            $message->to($singleTo, $toName);
                        } else {
                            $message->to($singleTo);
                        }
                    }
                    $message->subject($subject);
                    if (! empty($log->reply_to)) {
                        $message->replyTo($log->reply_to);
                    }

                    // Attachments for raw mail
                    $attachments = (array) ($log->attachments ?? []);
                    foreach ($attachments as $att) {
                        try {
                            $disk = $att['disk'] ?? 'public';
                            $rawPath = $att['path'] ?? null;
                            if (! $rawPath) {
                                continue;
                            }
                            $name = $att['name'] ?? basename($rawPath);
                            $mime = $att['mime'] ?? 'application/octet-stream';

                            $norm = ltrim(str_replace('\\', '/', (string) $rawPath), '/');
                            $candidates = [$norm];
                            if (str_starts_with($norm, 'public/')) {
                                $candidates[] = substr($norm, 7);
                            }
                            if (str_starts_with($norm, 'storage/')) {
                                $candidates[] = substr($norm, 8);
                            }
                            if (config('app.debug')) {
                                try {
                                    \Log::debug('EmailSender: attachment diagnostics (Laravel raw)', [
                                        'log_id' => $log->id,
                                        'disk' => $disk,
                                        'disk_driver' => config('filesystems.disks.'.$disk.'.driver'),
                                        'raw_path' => $rawPath,
                                        'normalized' => $norm,
                                        'candidates' => array_values(array_unique($candidates)),
                                    ]);
                                } catch (\Throwable $e) {
                                }
                            }
                            $attached = false;
                            foreach (array_values(array_unique($candidates)) as $cand) {
                                try {
                                    $message->attachFromStorageDisk($disk, $cand, $name, ['mime' => $mime]);
                                    if (config('app.debug')) {
                                        try {
                                            \Log::debug('EmailSender: attached via attachFromStorageDisk (Laravel raw)', [
                                                'log_id' => $log->id,
                                                'disk' => $disk,
                                                'candidate' => $cand,
                                                'mime' => $mime,
                                                'name' => $name,
                                            ]);
                                        } catch (\Throwable $e) {
                                        }
                                    }
                                    $attached = true;
                                    break;
                                } catch (\Throwable $e) {
                                }
                            }
                            if (! $attached) {
                                $storage = \Storage::disk($disk);
                                foreach (array_values(array_unique($candidates)) as $cand) {
                                    try {
                                        $exists = $storage->exists($cand);
                                        if (config('app.debug')) {
                                            try {
                                                \Log::debug('EmailSender: storage exists check (Laravel raw)', [
                                                    'log_id' => $log->id,
                                                    'disk' => $disk,
                                                    'candidate' => $cand,
                                                    'exists' => $exists,
                                                ]);
                                            } catch (\Throwable $e) {
                                            }
                                        }
                                        if ($exists) {
                                            $bytes = $storage->get($cand);
                                            if (! is_null($bytes) && $bytes !== false) {
                                                if (config('app.debug')) {
                                                    try {
                                                        \Log::debug('EmailSender: attaching from bytes (Laravel raw)', [
                                                            'log_id' => $log->id,
                                                            'disk' => $disk,
                                                            'candidate' => $cand,
                                                            'byte_length' => is_string($bytes) ? strlen($bytes) : null,
                                                            'mime' => $mime,
                                                            'name' => $name,
                                                        ]);
                                                    } catch (\Throwable $e) {
                                                    }
                                                }
                                                $message->attachData($bytes, $name, ['mime' => $mime]);
                                                break;
                                            }
                                        }
                                    } catch (\Throwable $e) {
                                    }
                                }
                            }
                        } catch (\Throwable $e) {
                        }
                    }
                });
            }
        }

        return ['message_id' => $messageId];
    }
}
