<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\MailingList;
use App\Models\MailingContact;
use App\Models\MailingMessaggio;
use App\Models\SenderAccount;
use App\Models\Individuo;
use App\Models\Documento;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;

class MailingController extends Controller
{
    public function nuovo(Request $request)
    {
        $this->authorizeWrite('mailing');
        $individui = collect();
        $individuiIds = $request->input('individui');

        if ($individuiIds) {
            $ids = array_map('trim', explode(',', $individuiIds));
            $individui = Individuo::with('contatti')->whereIn('id', $ids)->get();
        }

        $liste = MailingList::all();
        $documenti = Documento::orderBy('nome_file')->get();
        $senderAccounts = SenderAccount::active()->get();

        $documentiSelezionati = collect();
        if ($request->has('documenti_selezionati')) {
            $docIds = array_filter(array_map('trim', explode(',', $request->input('documenti_selezionati'))));
            $documentiSelezionati = Documento::whereIn('id', $docIds)->get();
        }

        return view('mailing.nuovo', compact('individui', 'liste', 'documenti', 'documentiSelezionati', 'senderAccounts'));
    }

    public function invia(Request $request)
    {
        $this->authorizeWrite('mailing');
        $data = $request->validate([
            'oggetto' => 'required|string|max:255',
            'corpo' => 'required',
            'lista_id' => 'nullable|exists:mailing_liste,id',
            'destinatari_ids' => 'nullable|string',
            'documenti_selezionati' => 'nullable|string',
            'allegato' => 'nullable|file|max:10240',
            'mittente_id' => 'nullable|integer|exists:sender_accounts,id',
        ]);

        $sender = null;
        if (!empty($data['mittente_id'])) {
            $sender = SenderAccount::findOrFail($data['mittente_id']);
        }

        $documentiAllegati = [];

        if ($request->hasFile('allegato')) {
            $file = $request->file('allegato');
            $nomeOriginale = $file->getClientOriginalName();
            $path = $file->store('documenti/allegati');

            $documento = Documento::create([
                'nome_file' => $nomeOriginale,
                'file_path' => $path,
                'tipo' => 'allegato',
                'tipologia' => 'allegato_email',
                'visibilita' => 'pubblico',
                'dimensione' => $file->getSize(),
                'mime_type' => $file->getMimeType(),
                'user_id' => auth()->id(),
            ]);

            $documentiAllegati[] = $documento->id;
        }

        if (!empty($data['documenti_selezionati'])) {
            $docIds = array_filter(array_map('trim', explode(',', $data['documenti_selezionati'])));
            $documentiAllegati = array_merge($documentiAllegati, $docIds);
        }

        $destinatari = collect();
        if (!empty($data['destinatari_ids'])) {
            $ids = array_filter(array_map('trim', explode(',', $data['destinatari_ids'])));
            $destinatari = Individuo::with('contatti')->whereIn('id', $ids)->get();
        } elseif (!empty($data['lista_id'])) {
            $lista = MailingList::with('contatti.individuo.contatti')->find($data['lista_id']);
            if ($lista) {
                $destinatari = $lista->contatti->filter(fn($c) => $c->opt_in)->map(fn($c) => $c->individuo)->filter();
            }
        }

        $emails = $destinatari->flatMap(fn($ind) => $ind->contatti->where('tipo', 'email')->pluck('valore'))
            ->filter()->unique()->values();

        if ($emails->isEmpty()) {
            return back()->with('error', 'Nessun destinatario email valido trovato.')->withInput();
        }

        $attachmentPaths = $this->resolveMailingAttachmentPaths($documentiAllegati);

        $messaggio = MailingMessaggio::create([
            'mailing_list_id' => $data['lista_id'] ?? null,
            'user_id' => auth()->id(),
            'oggetto' => $data['oggetto'],
            'corpo' => $data['corpo'],
            'stato' => 'in_coda',
            'totale_destinatari' => $emails->count(),
            'mittente_nome' => $sender?->email_name,
            'mittente_email' => $sender?->email_address,
        ]);

        $ok = 0;
        $fail = 0;
        $logFalliti = [];

        foreach ($emails as $email) {
            try {
                if ($sender) {
                    $sender->sendEmail($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
                } else {
                    $this->sendViaSystem($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
                }
                $ok++;
            } catch (\Throwable $e) {
                $fail++;
                $logFalliti[] = ['email' => $email, 'errore' => $e->getMessage()];
                Log::error('Mailing send failed', ['email' => $email, 'error' => $e->getMessage()]);
            }
        }

        $stato = $fail === 0 ? 'inviato' : ($ok > 0 ? 'parziale' : 'fallito');
        $messaggio->update([
            'stato' => $stato,
            'invii_ok' => $ok,
            'invii_falliti' => $fail,
            'log_falliti' => $logFalliti,
            'inviato_al' => now(),
        ]);

        $senderLabel = $sender ? '"' . $sender->email_address . '"' : 'sistema predefinito';

        if ($sender && $sender->verify_email && ($ok > 0 || $stato === 'fallito')) {
            $reportSubject = '✅ Report Invio - ' . $data['oggetto'];
            $reportBody = "Report invio mailing\n"
                . "Mittente: {$sender->email_name} <{$sender->email_address}>\n"
                . "Oggetto: {$data['oggetto']}\n"
                . "Totale destinatari: {$emails->count()}\n"
                . "Inviate con successo: {$ok}\n"
                . "Fallite: {$fail}\n";
            if (!empty($logFalliti)) {
                $reportBody .= "\nDettaglio errori:\n";
                foreach ($logFalliti as $f) {
                    $reportBody .= "- {$f['email']}: {$f['errore']}\n";
                }
            }
            $sender->sendReport($reportSubject, $reportBody);
        }

        $msg = "Invio completato tramite {$senderLabel}: {$ok} inviate, {$fail} fallite.";
        if ($ok > 0) {
            return redirect('/mailing-liste')->with('success', $msg);
        }

        return back()->with('error', $msg);
    }

    public function invio(Request $request)
    {
        $this->authorizeWrite('mailing');
        $liste = MailingList::where('attiva', true)->orderBy('nome')->get();
        $documenti = Documento::orderBy('nome_file')->get();
        $senderAccounts = SenderAccount::active()->get();

        return view('mailing.invio', compact('liste', 'documenti', 'senderAccounts'));
    }

    public function invioElabora(Request $request)
    {
        $this->authorizeWrite('mailing');
        $data = $request->validate([
            'liste' => 'required|array',
            'liste.*' => 'exists:mailing_liste,id',
            'oggetto' => 'required|string|max:255',
            'corpo' => 'required',
            'documenti_selezionati' => 'nullable|array',
            'documenti_selezionati.*' => 'exists:documenti,id',
            'allegato' => 'nullable|file|max:10240',
            'mittente_id' => 'nullable|integer|exists:sender_accounts,id',
        ]);

        $sender = null;
        if (!empty($data['mittente_id'])) {
            $sender = SenderAccount::findOrFail($data['mittente_id']);
        }

        $documentiAllegati = [];

        if ($request->hasFile('allegato')) {
            $file = $request->file('allegato');
            $nomeOriginale = $file->getClientOriginalName();
            $path = $file->store('documenti/allegati');

            $documento = Documento::create([
                'nome_file' => $nomeOriginale,
                'file_path' => $path,
                'tipo' => 'allegato',
                'tipologia' => 'allegato_email',
                'visibilita' => 'pubblico',
                'dimensione' => $file->getSize(),
                'mime_type' => $file->getMimeType(),
                'user_id' => auth()->id(),
            ]);

            $documentiAllegati[] = $documento->id;
        }

        if (!empty($data['documenti_selezionati'])) {
            $documentiAllegati = array_merge($documentiAllegati, $data['documenti_selezionati']);
        }

        $contatti = MailingContact::whereIn('mailing_list_id', $data['liste'])
            ->where('opt_in', true)
            ->with('individuo.contatti')
            ->get();

        $emails = $contatti->map(fn($c) => $c->individuo?->contatti->where('tipo', 'email')->first()?->valore)
            ->filter()->unique()->values();

        if ($emails->isEmpty()) {
            return back()->with('error', 'Nessun destinatario email valido trovato nelle liste selezionate.');
        }

        $attachmentPaths = $this->resolveMailingAttachmentPaths($documentiAllegati);

        $listeNomi = MailingList::whereIn('id', $data['liste'])->pluck('nome')->implode(', ');

        $messaggio = MailingMessaggio::create([
            'mailing_list_id' => null,
            'user_id' => auth()->id(),
            'oggetto' => $data['oggetto'],
            'corpo' => $data['corpo'],
            'stato' => 'in_coda',
            'totale_destinatari' => $emails->count(),
            'mittente_nome' => $sender?->email_name,
            'mittente_email' => $sender?->email_address,
        ]);

        $ok = 0;
        $fail = 0;
        $logFalliti = [];

        foreach ($emails as $email) {
            try {
                if ($sender) {
                    $sender->sendEmail($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
                } else {
                    $this->sendViaSystem($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
                }
                $ok++;
            } catch (\Throwable $e) {
                $fail++;
                $logFalliti[] = ['email' => $email, 'errore' => $e->getMessage()];
                Log::error('Mailing bulk send failed', ['email' => $email, 'error' => $e->getMessage()]);
            }
        }

        $stato = $fail === 0 ? 'inviato' : ($ok > 0 ? 'parziale' : 'fallito');
        $messaggio->update([
            'stato' => $stato,
            'invii_ok' => $ok,
            'invii_falliti' => $fail,
            'log_falliti' => $logFalliti,
            'inviato_al' => now(),
        ]);

        $senderLabel = $sender ? '"' . $sender->email_address . '"' : 'sistema predefinito';

        if ($sender && $sender->verify_email && ($ok > 0 || $stato === 'fallito')) {
            $reportSubject = '✅ Report Invio Massivo - ' . $data['oggetto'];
            $reportBody = "Report invio massivo\n"
                . "Mittente: {$sender->email_name} <{$sender->email_address}>\n"
                . "Liste: {$listeNomi}\n"
                . "Oggetto: {$data['oggetto']}\n"
                . "Totale destinatari: {$emails->count()}\n"
                . "Inviate con successo: {$ok}\n"
                . "Fallite: {$fail}\n";
            if (!empty($logFalliti)) {
                $reportBody .= "\nDettaglio errori:\n";
                foreach ($logFalliti as $f) {
                    $reportBody .= "- {$f['email']}: {$f['errore']}\n";
                }
            }
            $sender->sendReport($reportSubject, $reportBody);
        }

        $msg = "Invio massivo completato tramite {$senderLabel}: {$ok} inviate, {$fail} fallite.";
        if ($ok > 0) {
            return redirect('/')->with('success', $msg);
        }

        return back()->with('error', $msg);
    }

    private function sendViaSystem(string $to, string $subject, string $body, array $attachmentPaths = []): void
    {
        $settings = \App\Models\EmailSetting::getActive();
        if (!$settings) {
            throw new \Exception('Account email principale non configurato.');
        }

        $smtpPassword = $settings->getDecryptedSmtpPassword() ?? $settings->getDecryptedPassword();
        $encryption = $settings->smtp_encryption ?? 'tls';
        $scheme = ($encryption === 'ssl') ? 'smtps' : 'smtp';

        $dsn = sprintf(
            '%s://%s:%s@%s:%d?encryption=%s',
            $scheme,
            rawurlencode($settings->smtp_username ?? $settings->email_address),
            rawurlencode($smtpPassword),
            $settings->smtp_host ?? 'smtp.gmail.com',
            $settings->smtp_port ?? 587,
            $encryption
        );

        $transport = \Symfony\Component\Mailer\Transport::fromDsn($dsn);
        $mailer = new \Symfony\Component\Mailer\Mailer($transport);

        $fromName = $settings->email_name ?? '';
        if (filter_var($fromName, FILTER_VALIDATE_EMAIL)) {
            $fromName = 'Glastree';
        } else {
            $fromName = preg_replace('/[^\p{L}\p{N}\s]/u', '', $fromName);
            $fromName = trim($fromName);
            if (empty($fromName) || strlen($fromName) > 50) {
                $fromName = 'Glastree';
            }
        }

        $fromAddress = \Symfony\Component\Mime\Address::create($settings->email_address, $fromName);

        $email = (new \Symfony\Component\Mime\Email())
            ->from($fromAddress)
            ->to($to)
            ->subject($subject)
            ->text($body);

        foreach ($attachmentPaths as $path) {
            $email->attachFromPath($path);
        }

        $mailer->send($email);
    }

    private function resolveMailingAttachmentPaths(array $documentIds): array
    {
        $paths = [];
        if (empty($documentIds)) {
            return $paths;
        }

        $docs = Documento::whereIn('id', $documentIds)->get();
        foreach ($docs as $doc) {
            if ($doc->file_path && Storage::exists($doc->file_path)) {
                $paths[] = Storage::path($doc->file_path);
            }
        }

        return $paths;
    }
}
