<?php

namespace App\Http\Controllers;

use App\Jobs\ProcessCampaignJob;
use App\Mail\CampaignTestMail;
use App\Models\Campaign;
use App\Models\ContactList;
use App\Models\SmtpConfig;
use App\Models\Tag;
use App\Services\AdvancedFilter\FilterFieldRegistry;
use App\Services\AdvancedFilter\FilterQueryBuilder;
use App\Services\AdvancedFilter\InvalidFilterException;
use App\Services\CampaignRecipientResolver;
use App\Services\EmailRenderer;
use App\Services\MediaUploadService;
use App\Services\SmtpConfigService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Inertia\Inertia;
use Inertia\Response;

class CampaignController extends Controller
{
    public function index(Request $request): Response
    {
        $query = Campaign::query()
            ->where('type', 'campaign')
            ->orderByDesc('updated_at')
            ->orderByDesc('id');

        if ($search = $request->input('search')) {
            $query->where('title', 'like', "%{$search}%");
        }

        if ($status = $request->input('status')) {
            $query->where('status', $status);
        }

        $campaigns = $query->paginate(20)->withQueryString();

        return Inertia::render('Campaigns/Index', [
            'campaigns' => $campaigns->through(fn($c) => [
                'id'               => $c->id,
                'title'            => $c->title,
                'status'           => $c->status,
                'email_subject'    => $c->email_subject,
                'recipients_count' => $c->recipients_count,
                'emails_sent'        => $c->emails_sent        ?? 0,
                'emails_failed'      => $c->emails_failed      ?? 0,
                'emails_bounced'     => $c->emails_bounced      ?? 0,
                'unique_opens'       => $c->unique_opens        ?? 0,
                'unique_clicks'      => $c->unique_clicks       ?? 0,
                'unsubscribes_count' => $c->unsubscribes_count  ?? 0,
                'conversions_count'  => $c->conversions_count   ?? 0,
                'scheduled_at'       => $c->scheduled_at?->toISOString(),
                'send_mode'          => $c->send_mode,
                'updated_at'       => $c->updated_at->toISOString(),
                'created_at'       => $c->created_at->toISOString(),
            ]),
            'filters' => $request->only(['search', 'status']),
        ]);
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'title' => 'required|string|max:192',
        ]);

        $campaign = Campaign::create([
            'title'      => $data['title'],
            'status'     => 'draft',
            'created_by' => auth()->id(),
        ]);

        return redirect()->route('campaigns.edit', $campaign)
            ->with('success', 'Campaign created.');
    }

    public function edit(Campaign $campaign): Response
    {
        abort_unless($campaign->type === 'campaign', 404);

        $lists = ContactList::withCount('contacts')->orderBy('title')->get(['id', 'title']);
        $tags  = Tag::withCount('contacts')->orderBy('title')->get(['id', 'title']);

        // Sender identity comes from the default SMTP profile's From details
        $defaultSmtp = SmtpConfig::where('is_default', true)->first();
        $smtpConfigs = SmtpConfig::orderByDesc('is_default')
            ->orderBy('name')
            ->get(['id', 'name', 'provider', 'from_email', 'from_name', 'is_default']);

        return Inertia::render('Campaigns/Create', [
            'campaign'       => [
                'id'                => $campaign->id,
                'title'             => $campaign->title,
                'status'            => $campaign->status,
                'email_subject'     => $campaign->email_subject,
                'email_pre_header'  => $campaign->email_pre_header,
                'utm_status'        => $campaign->utm_status,
                'utm_source'        => $campaign->utm_source,
                'utm_medium'        => $campaign->utm_medium,
                'utm_campaign'      => $campaign->utm_campaign,
                'utm_term'          => $campaign->utm_term,
                'utm_content'       => $campaign->utm_content,
                'recipients_config' => $campaign->recipients_config ?? ['mode' => 'list_tag', 'included' => [['list_id' => null, 'tag_id' => null]], 'excluded' => []],
                'recipients_count'  => $campaign->recipients_count,
                'settings'          => $campaign->settings ?? ['blocks' => []],
            ],
            'lists'           => $lists,
            'tags'            => $tags,
            'smtpConfigs'     => $smtpConfigs,
            'campaigns'       => Campaign::where('user_id', $campaign->user_id)->whereIn('type', ['campaign', 'recurring'])->orderByDesc('id')->get(['id', 'title']),
            'automations'     => Campaign::where('user_id', $campaign->user_id)->whereIn('type', ['automation', 'sequence'])->orderByDesc('id')->get(['id', 'title', 'type']),
            'filterFieldMenu' => FilterFieldRegistry::menuTree(),
            'defaultSender'   => $defaultSmtp ? [
                'profile'    => $defaultSmtp->name,
                'from_name'  => $defaultSmtp->from_name,
                'from_email' => $defaultSmtp->from_email,
            ] : null,
        ]);
    }

    public function update(Request $request, Campaign $campaign): JsonResponse
    {
        abort_unless($campaign->type === 'campaign', 404);

        $fillable = $campaign->getFillable();
        $data     = [];

        // Accept any fillable field
        foreach ($fillable as $field) {
            if ($request->has($field)) {
                $data[$field] = $request->input($field);
            }
        }

        // Merge settings deeply rather than overwrite
        if ($request->has('settings')) {
            $current            = $campaign->settings ?? [];
            $data['settings']   = array_merge($current, (array) $request->input('settings'));

            if (isset($data['settings']['blocks'])) {
                $renderer = app(\App\Services\EmailRenderer::class);
                if ($campaign->utm_status) {
                    $renderer->setUtmData([
                        'utm_source'   => $campaign->utm_source,
                        'utm_medium'   => $campaign->utm_medium,
                        'utm_campaign' => $campaign->utm_campaign,
                        'utm_term'     => $campaign->utm_term,
                        'utm_content'  => $campaign->utm_content,
                    ]);
                }
                $data['email_body'] = $renderer->render($data['settings']['blocks'], [], $campaign->id);
            }
        }

        if (!empty($data)) {
            $campaign->update($data);
        }

        return response()->json(['success' => true, 'updated_at' => $campaign->fresh()->updated_at->toISOString()]);
    }

    public function sendTest(Request $request, Campaign $campaign, EmailRenderer $renderer): JsonResponse
    {
        abort_unless($campaign->type === 'campaign', 404);

        $request->validate([
            'email'          => 'required|email|max:255',
            'smtp_config_id' => 'nullable|integer',
        ]);

        $blocks = $request->input('blocks') ?? $campaign->settings['blocks'] ?? [];
        if ($campaign->utm_status) {
            $renderer->setUtmData([
                'utm_source'   => $campaign->utm_source,
                'utm_medium'   => $campaign->utm_medium,
                'utm_campaign' => $campaign->utm_campaign,
                'utm_term'     => $campaign->utm_term,
                'utm_content'  => $campaign->utm_content,
            ]);
        }
        $html   = $renderer->render($blocks, [], $campaign->id);

        try {
            // Send through the configured SMTP profile when one exists
            $smtpId = $campaign->settings['smtp_config_id'] ?? null;
            $smtp = null;
            if ($smtpId) {
                $smtp = SmtpConfig::find($smtpId);
            }
            if (!$smtp) {
                $smtp = SmtpConfigService::getDefault();
            }
            if ($smtp) {
                SmtpConfigService::configureRuntime($smtp);
            }

            // Same From/Reply-To precedence a real send uses, including the
            // Settings > Email Settings > Default From Settings fallback 
            // a test send should look like the real thing.
            $identity = SmtpConfigService::resolveSenderIdentity($campaign, $smtp);

            Mail::to($request->input('email'))
                ->send(new CampaignTestMail(
                    $campaign,
                    $html,
                    $identity['from_email'],
                    $identity['from_name'],
                    $identity['reply_email'],
                    $identity['reply_name'],
                ));
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
        }

        return response()->json(['success' => true]);
    }

    public function broadcast(Request $request, Campaign $campaign): JsonResponse
    {
        // Also allows a materialized recurring occurrence to be triggered
        // manually ("Send Now" on a draft row when auto_send is off).
        abort_unless(in_array($campaign->type, ['campaign', 'recurring_occurrence'], true), 404);

        $request->validate([
            'send_mode'    => 'required|in:now,schedule,range',
            'scheduled_at' => 'nullable|date|required_if:send_mode,schedule',
            'range_start'  => 'nullable|date|required_if:send_mode,range',
            'range_end'    => 'nullable|date|required_if:send_mode,range|after:range_start',
        ]);

        $mode = $request->input('send_mode');

        // Snapshot the sender identity (the campaign's selected profile, or
        // the tenant default if none was picked) for reporting
        $smtp     = SmtpConfigService::resolveForCampaign($campaign);
        $settings = $campaign->settings ?? [];
        $settings['from_name']  = $smtp?->from_name;
        $settings['from_email'] = $smtp?->from_email;

        $campaign->update([
            'settings'     => $settings,
            'send_mode'    => $mode,
            'scheduled_at' => $mode === 'schedule' ? $request->input('scheduled_at') : null,
            'range_start'  => $mode === 'range'    ? $request->input('range_start')  : null,
            'range_end'    => $mode === 'range'    ? $request->input('range_end')    : null,
            'status'       => $mode === 'now' ? 'queued' : 'scheduled',
            'emails_sent'  => 0,
            'emails_failed'=> 0,
        ]);

        ProcessCampaignJob::dispatch($campaign);

        $message = match ($mode) {
            'schedule' => 'Campaign scheduled for ' . $campaign->fresh()->scheduled_at?->format('M j, Y g:i A') . '.',
            'range'    => 'Campaign queued - emails will be spread across the selected time range.',
            default    => 'Campaign is being sent. Emails are dispatched in the background.',
        };

        return response()->json(['success' => true, 'message' => $message, 'status' => $campaign->status]);
    }

    public function countRecipients(Request $request): JsonResponse
    {
        $config = $request->all();

        // Advanced-filter mode is validated directly (rather than through
        // CampaignRecipientResolver::count(), which fails closed/silent for
        // the send path) so this endpoint can surface a real 422 instead of
        // an indistinguishable "0 contacts found".
        if (($config['mode'] ?? 'list_tag') === 'advanced_filter') {
            try {
                $count = (new FilterQueryBuilder(auth()->id()))->build($config['filter'] ?? [])->count();
            } catch (InvalidFilterException $e) {
                return response()->json(['message' => $e->getMessage()], 422);
            }

            return response()->json(['count' => $count]);
        }

        return response()->json(['count' => CampaignRecipientResolver::count($config, auth()->id())]);
    }

    private function eventPage(int $campaignId, string $type, string $pageName)
    {
        return \App\Models\CampaignEvent::with('contact:id,email,first_name,last_name,status')
            ->where('campaign_id', $campaignId)
            ->where('event_type', $type)
            ->orderByDesc('id')
            ->paginate(25, ['*'], $pageName)
            ->withQueryString()
            ->through(fn($e) => [
                'id'         => $e->id,
                'contact_id' => $e->contact_id,
                'email'      => $e->contact?->email,
                'name'       => trim(($e->contact?->first_name ?? '') . ' ' . ($e->contact?->last_name ?? '')),
                'status'     => $e->contact?->status,
                'url'        => $e->url,
                'reason'     => $e->reason,
                // Opens keep one row per contact; updated_at is the last open
                'at'         => ($type === 'open' ? $e->updated_at : $e->created_at)->toISOString(),
            ]);
    }

    public function show(Campaign $campaign): Response
    {
        // Also viewable for a materialized recurring occurrence ("View Report").
        abort_unless(in_array($campaign->type, ['campaign', 'recurring_occurrence'], true), 404);

        $settings = $campaign->settings ?? [];
        $parent   = $campaign->parent_id ? Campaign::find($campaign->parent_id) : null;

        return Inertia::render('Campaigns/Show', [
            'deliveries'   => $this->eventPage($campaign->id, 'sent',        'deliveries_page'),
            'opens'        => $this->eventPage($campaign->id, 'open',        'opens_page'),
            'clicks'       => $this->eventPage($campaign->id, 'click',       'clicks_page'),
            'unsubscribes' => $this->eventPage($campaign->id, 'unsubscribe', 'unsubscribes_page'),
            'bounces'      => $this->eventPage($campaign->id, 'bounced',     'bounces_page'),
            'campaign' => [
                'id'               => $campaign->id,
                'title'            => $campaign->title,
                'status'           => $campaign->status,
                'email_subject'    => $campaign->email_subject,
                'email_pre_header' => $campaign->email_pre_header,
                'send_mode'        => $campaign->send_mode,
                'scheduled_at'     => $campaign->scheduled_at?->toISOString(),
                'range_start'      => $campaign->range_start?->toISOString(),
                'range_end'        => $campaign->range_end?->toISOString(),
                'recipients_count'   => $campaign->recipients_count  ?? 0,
                'emails_sent'        => $campaign->emails_sent        ?? 0,
                'emails_failed'      => $campaign->emails_failed      ?? 0,
                'emails_bounced'     => $campaign->emails_bounced      ?? 0,
                'opens_count'        => $campaign->opens_count         ?? 0,
                'unique_opens'       => $campaign->unique_opens        ?? 0,
                'clicks_count'       => $campaign->clicks_count        ?? 0,
                'unique_clicks'      => $campaign->unique_clicks       ?? 0,
                'unsubscribes_count' => $campaign->unsubscribes_count  ?? 0,
                'from_name'          => $settings['from_name']         ?? null,
                'from_email'       => $settings['from_email']    ?? null,
                'reply_to_email'   => $settings['reply_to_email'] ?? null,
                'created_at'       => $campaign->created_at->toISOString(),
                'updated_at'       => $campaign->updated_at->toISOString(),
            ],
            'parentCampaign' => $parent ? ['id' => $parent->id, 'title' => $parent->title] : null,
        ]);
    }

    public function preview(Campaign $campaign, EmailRenderer $renderer): JsonResponse
    {
        abort_unless(in_array($campaign->type, ['campaign', 'recurring_occurrence'], true), 404);

        $blocks = $campaign->settings['blocks'] ?? [];
        if ($campaign->utm_status) {
            $renderer->setUtmData([
                'utm_source'   => $campaign->utm_source,
                'utm_medium'   => $campaign->utm_medium,
                'utm_campaign' => $campaign->utm_campaign,
                'utm_term'     => $campaign->utm_term,
                'utm_content'  => $campaign->utm_content,
            ]);
        }

        return response()->json([
            'subject'          => $campaign->email_subject ?: $campaign->title,
            'recipients_count' => $campaign->recipients_count ?? 0,
            'created_at'       => $campaign->created_at->toISOString(),
            'html'             => $renderer->render($blocks),
        ]);
    }

    public function duplicate(Campaign $campaign)
    {
        abort_unless($campaign->type === 'campaign', 404);

        $copy = $campaign->replicate([
            'slug', 'status', 'send_mode',
            'scheduled_at', 'range_start', 'range_end',
            'emails_sent', 'emails_failed',
            'opens_count', 'unique_opens',
            'clicks_count', 'unique_clicks',
            'unsubscribes_count', 'conversions_count',
        ]);

        $copy->title  = 'Copy of ' . $campaign->title;
        $copy->status = 'draft';
        $copy->slug   = null; // let booted() generate a unique slug

        $copy->save();

        return redirect()->route('campaigns.edit', $copy)->with('success', 'Campaign duplicated.');
    }

    public function destroy(Campaign $campaign)
    {
        abort_unless($campaign->type === 'campaign', 404);

        $campaign->delete();
        return redirect()->route('campaigns.index')->with('success', 'Campaign deleted.');
    }

    public function bulkDestroy(Request $request)
    {
        $data = $request->validate([
            'ids'   => 'required|array|min:1',
            'ids.*' => 'integer|exists:campaigns,id',
        ]);

        $count = Campaign::whereIn('id', $data['ids'])->where('type', 'campaign')->delete();

        return redirect()->route('campaigns.index')
            ->with('success', $count . ' ' . str('campaign')->plural($count) . ' deleted.');
    }

    public function uploadImage(Request $request, MediaUploadService $uploads): JsonResponse
    {
        $request->validate([
            'image' => 'required|image|mimes:jpg,jpeg,png,gif,webp,svg|max:8192',
        ]);

        $media = $uploads->store($request->file('image'), 'campaign-image');

        return response()->json([
            'url'  => $media->url,
            'name' => $media->filename,
            'size' => $media->size,
        ]);
    }

    public function uploadVideo(Request $request, MediaUploadService $uploads): JsonResponse
    {
        $request->validate([
            'video' => 'required|file|mimes:mp4,webm,ogg,mov|max:102400',
        ]);

        $media = $uploads->store($request->file('video'), 'campaign-video');

        return response()->json([
            'url'  => $media->url,
            'name' => $media->filename,
            'size' => $media->size,
        ]);
    }
}
