<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Mail\BroadcastEmail;
use App\Models\EmailBroadcast;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class EmailBroadcastController extends Controller
{
    public function index(): View
    {
        return view('admin.email-broadcasts.index', [
            'broadcasts' => EmailBroadcast::query()
                ->with('creator')
                ->latest()
                ->paginate(20),
        ]);
    }

    public function create(): View
    {
        return view('admin.email-broadcasts.create', [
            'recipientGroups' => EmailBroadcast::recipientGroups(),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $data = $this->validatedData($request);
        $action = $request->input('action', 'draft');
        unset($data['action']);

        if ($action === 'test') {
            $draft = new EmailBroadcast($data + [
                'status' => EmailBroadcast::STATUS_DRAFT,
                'created_by' => $request->user()->id,
            ]);

            try {
                Mail::to($request->user()->email)->send(new BroadcastEmail($draft, $request->user()));
            } catch (\Throwable $exception) {
                Log::error('Broadcast test email failed.', [
                    'admin_id' => $request->user()->id,
                    'message' => $exception->getMessage(),
                ]);

                return back()
                    ->withInput()
                    ->with('error', 'Test email failed. Check your SMTP settings and try again.');
            }

            return back()
                ->withInput()
                ->with('success', 'Test email sent to your admin email address.');
        }

        $broadcast = EmailBroadcast::create($data + [
            'status' => EmailBroadcast::STATUS_DRAFT,
            'created_by' => $request->user()->id,
        ]);

        if ($action === 'send') {
            return $this->sendBroadcast($broadcast);
        }

        return redirect()
            ->route('admin.email-broadcasts.show', $broadcast)
            ->with('success', 'Email broadcast saved as draft.');
    }

    public function show(EmailBroadcast $emailBroadcast): View
    {
        return view('admin.email-broadcasts.show', [
            'broadcast' => $emailBroadcast->load('creator'),
            'recipientGroups' => EmailBroadcast::recipientGroups(),
        ]);
    }

    public function send(EmailBroadcast $emailBroadcast): RedirectResponse
    {
        if ($emailBroadcast->status !== EmailBroadcast::STATUS_DRAFT) {
            return back()->with('error', 'Only draft broadcasts can be sent.');
        }

        return $this->sendBroadcast($emailBroadcast);
    }

    private function validatedData(Request $request): array
    {
        return $request->validate([
            'subject' => ['required', 'string', 'min:3', 'max:180'],
            'message' => ['required', 'string', 'min:5', 'max:20000'],
            'recipient_group' => ['required', Rule::in(array_keys(EmailBroadcast::recipientGroups()))],
            'action' => ['nullable', Rule::in(['draft', 'send', 'test'])],
        ]);
    }

    private function sendBroadcast(EmailBroadcast $broadcast): RedirectResponse
    {
        $query = $this->recipientQuery($broadcast->recipient_group);
        $totalRecipients = (clone $query)->count();
        $sentCount = 0;
        $failedCount = 0;
        $useQueue = config('queue.default') !== 'sync';

        $broadcast->update([
            'status' => EmailBroadcast::STATUS_SENDING,
            'total_recipients' => $totalRecipients,
            'sent_count' => 0,
            'failed_count' => 0,
        ]);

        $query->chunkById(100, function ($users) use ($broadcast, $useQueue, &$sentCount, &$failedCount): void {
            foreach ($users as $user) {
                try {
                    $mail = Mail::to($user->email);

                    if ($useQueue) {
                        $mail->queue(new BroadcastEmail($broadcast, $user));
                    } else {
                        $mail->send(new BroadcastEmail($broadcast, $user));
                    }

                    $sentCount++;
                } catch (\Throwable $exception) {
                    $failedCount++;

                    Log::error('Broadcast email failed for recipient.', [
                        'broadcast_id' => $broadcast->id,
                        'user_id' => $user->id,
                        'message' => $exception->getMessage(),
                    ]);
                }
            }
        });

        $broadcast->update([
            'status' => $failedCount > 0 ? EmailBroadcast::STATUS_FAILED : EmailBroadcast::STATUS_SENT,
            'sent_count' => $sentCount,
            'failed_count' => $failedCount,
            'sent_at' => now(),
        ]);

        return redirect()
            ->route('admin.email-broadcasts.show', $broadcast)
            ->with('success', "Broadcast processed. Sent: {$sentCount}. Failed: {$failedCount}.");
    }

    private function recipientQuery(string $group): Builder
    {
        $query = User::query()
            ->whereNotNull('email')
            ->orderBy('id');

        return match ($group) {
            EmailBroadcast::GROUP_VERIFIED_USERS => $query->whereNotNull('email_verified_at'),
            EmailBroadcast::GROUP_UNVERIFIED_USERS => $query->whereNull('email_verified_at'),
            EmailBroadcast::GROUP_PREMIUM_USERS => $query->whereHas('subscriptions', fn ($query) => $query->active()),
            EmailBroadcast::GROUP_FREE_USERS => $query->whereDoesntHave('subscriptions', fn ($query) => $query->active()),
            EmailBroadcast::GROUP_MENTORS => $query->where('role', 'mentor'),
            EmailBroadcast::GROUP_SUPPORT_TEAM => $query->where('role', 'support_team'),
            default => $query,
        };
    }
}
