<?php

namespace App\Services\Mail;

use App\Models\EmailLog;
use App\Models\GeneratedReport;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use RuntimeException;
use Throwable;

class ReportMailer
{
    public function __construct(
        private readonly MailSettingsManager $settingsManager,
        private readonly ReportEmailBuilder $emailBuilder,
    ) {
    }

    /**
     * @param array<string, mixed> $options
     */
    public function sendReport(GeneratedReport $report, array $options = []): EmailLog
    {
        $report->loadMissing('client');

        if ($report->status !== 'completed') {
            throw new RuntimeException('Tamamlanmamış rapor gönderilemez. Önce raporu yeniden oluşturun.');
        }

        $recipients = $this->resolveRecipients($report, Arr::get($options, 'recipients', []));
        $cc = $this->normalizeEmails(Arr::get($options, 'cc', []));
        $bcc = $this->normalizeEmails(Arr::get($options, 'bcc', []));

        if ($recipients === []) {
            throw new RuntimeException('Rapor gönderimi için en az bir alıcı gerekir.');
        }

        $subject = $this->emailBuilder->subject($report, Arr::get($options, 'subject'));
        $attachments = $this->attachments($report, $options);

        $emailLog = EmailLog::create([
            'client_id' => $report->client_id,
            'generated_report_id' => $report->id,
            'subject' => $subject,
            'recipients' => $recipients,
            'cc' => $cc,
            'bcc' => $bcc,
            'status' => 'queued',
            'attachment_paths' => array_values($attachments),
        ]);

        try {
            $this->settingsManager->apply();
            $settings = $this->settingsManager->all();
            $viewData = $this->emailBuilder->viewData($report, Arr::get($options, 'note'));

            Mail::send('emails.reports.report-ready', $viewData, function ($message) use ($subject, $recipients, $cc, $bcc, $attachments, $settings) {
                $message->to($recipients)->subject($subject);

                if ($cc !== []) {
                    $message->cc($cc);
                }

                if ($bcc !== []) {
                    $message->bcc($bcc);
                }

                if (filled($settings['from_email'] ?? null)) {
                    $message->from($settings['from_email'], $settings['from_name'] ?? null);
                }

                if (filled($settings['reply_to'] ?? null)) {
                    $message->replyTo($settings['reply_to']);
                }

                foreach ($attachments as $label => $relativePath) {
                    if (Storage::disk('public')->exists($relativePath)) {
                        $message->attach(Storage::disk('public')->path($relativePath), [
                            'as' => basename($relativePath),
                            'mime' => $this->mimeFor($relativePath),
                        ]);
                    }
                }
            });

            $emailLog->update([
                'status' => 'sent',
                'sent_at' => now(),
                'error_message' => null,
            ]);

            $report->update(['sent_at' => now()]);
        } catch (Throwable $exception) {
            report($exception);

            $emailLog->update([
                'status' => 'failed',
                'error_message' => $exception->getMessage(),
            ]);
        }

        return $emailLog->refresh();
    }

    /**
     * @param string|array<int, string>|null $value
     * @return array<int, string>
     */
    public function normalizeEmails(string|array|null $value): array
    {
        if (is_array($value)) {
            $items = $value;
        } else {
            $items = preg_split('/\r\n|\r|\n|,|;/', (string) $value) ?: [];
        }

        return collect($items)
            ->map(fn ($email) => trim((string) $email))
            ->filter(fn ($email) => filter_var($email, FILTER_VALIDATE_EMAIL))
            ->unique()
            ->values()
            ->all();
    }

    /**
     * @param array<int, string>|string|null $explicitRecipients
     * @return array<int, string>
     */
    private function resolveRecipients(GeneratedReport $report, array|string|null $explicitRecipients): array
    {
        $recipients = $this->normalizeEmails($explicitRecipients);

        if ($recipients !== []) {
            return $recipients;
        }

        $clientRecipients = $report->client?->report_recipients ?: [];
        $recipients = $this->normalizeEmails($clientRecipients);

        if ($recipients !== []) {
            return $recipients;
        }

        return $this->normalizeEmails($report->client?->email);
    }

    /**
     * @param array<string, mixed> $options
     * @return array<string, string>
     */
    private function attachments(GeneratedReport $report, array $options): array
    {
        $includePdf = (bool) Arr::get($options, 'include_pdf', true);
        $includeExcel = (bool) Arr::get($options, 'include_excel', true);
        $includeCsv = (bool) Arr::get($options, 'include_csv', false);
        $includeHtml = (bool) Arr::get($options, 'include_html', false);

        $attachments = [];

        if ($includePdf && $report->pdf_path) {
            $attachments['pdf'] = $report->pdf_path;
        }

        if ($includePdf && ! $report->pdf_path && $report->html_path) {
            $attachments['html_fallback'] = $report->html_path;
        }

        if ($includeExcel && $report->excel_path) {
            $attachments['excel'] = $report->excel_path;
        }

        if ($includeCsv && $report->csv_path) {
            $attachments['csv'] = $report->csv_path;
        }

        if ($includeHtml && $report->html_path) {
            $attachments['html'] = $report->html_path;
        }

        return array_filter($attachments, fn ($path) => Storage::disk('public')->exists($path));
    }

    private function mimeFor(string $path): string
    {
        return match (pathinfo($path, PATHINFO_EXTENSION)) {
            'pdf' => 'application/pdf',
            'xls' => 'application/vnd.ms-excel',
            'csv' => 'text/csv',
            'html' => 'text/html',
            default => 'application/octet-stream',
        };
    }
}
