<?php

namespace App\Http\Controllers;

use App\Mail\CampaignTestMail;
use App\Models\Campaign;
use App\Models\CampaignEvent;
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\RecurringScheduleCalculator;
use App\Services\SmtpConfigService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Inertia\Inertia;
use Inertia\Response;

class RecurringCampaignController extends Controller
{
    // Fields that, when present in an update request, mean the schedule needs recomputing
    private const SCHEDULE_FIELDS = [
        'frequency', 'recurring_days', 'recurring_time', 'recurring_timezone',
        'start_date', 'end_type', 'end_date', 'end_count',
    ];

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

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

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

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

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

        $base = Campaign::where('type', 'recurring');

        $templateIds = (clone $base)->pluck('id');

        $stats = [
            'total'         => (clone $base)->count(),
            'active'        => (clone $base)->where('status', 'active')->count(),
            'paused'        => (clone $base)->where('status', 'paused')->count(),
            'draft'         => (clone $base)->where('status', 'draft')->count(),
            'sentThisMonth' => Campaign::where('type', 'recurring_occurrence')
                ->whereIn('parent_id', $templateIds)
                ->whereBetween('created_at', [now()->startOfMonth(), now()->endOfMonth()])
                ->sum('emails_sent'),
            'avgOpenRate'   => $this->avgOpenRate($templateIds->all()),
            'nextSend'      => (clone $base)->where('status', 'active')
                ->whereNotNull('next_run_at')
                ->orderBy('next_run_at')
                ->value('next_run_at'),
        ];

        return Inertia::render('Recurring/Index', [
            'campaigns' => $campaigns->through(fn($c) => [
                'id'               => $c->id,
                'title'            => $c->title,
                'status'           => $c->status,
                'frequency'        => $c->frequency,
                'recurring_days'   => $c->recurring_days,
                'recurring_time'   => $c->recurring_time,
                'recipients_count' => $c->recipients_count,
                'last_sent_at'     => $c->last_sent_at?->toISOString(),
                'next_run_at'      => $c->next_run_at?->toISOString(),
                'occurrences_sent' => $c->occurrences_sent,
                'open_rate'        => $this->avgOpenRate([$c->id]),
                'updated_at'       => $c->updated_at->toISOString(),
                'created_at'       => $c->created_at->toISOString(),
            ]),
            'stats'   => $stats,
            'filters' => $request->only(['search', 'status', 'frequency']),
        ]);
    }

    private function avgOpenRate(array $templateIds): float
    {
        if (empty($templateIds)) {
            return 0;
        }

        $occurrenceIds = Campaign::where('type', 'recurring_occurrence')
            ->whereIn('parent_id', $templateIds)
            ->pluck('id');

        if ($occurrenceIds->isEmpty()) {
            return 0;
        }

        $sent = Campaign::whereIn('id', $occurrenceIds)->sum('emails_sent');
        if ($sent === 0) {
            return 0;
        }

        $opens = CampaignEvent::whereIn('campaign_id', $occurrenceIds)
            ->where('event_type', 'open')
            ->distinct('contact_id')
            ->count('contact_id');

        return round(($opens / $sent) * 100, 1);
    }

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

        $campaign = Campaign::create([
            'title'              => $data['title'],
            'type'               => 'recurring',
            'status'             => 'draft',
            'frequency'          => 'weekly',
            'recurring_days'     => [1], // Monday
            'recurring_time'     => '09:00',
            'recurring_timezone' => config('app.timezone'),
            'auto_send'          => true,
            'end_type'           => 'never',
            'created_by'         => auth()->id(),
        ]);

        return redirect()->route('recurring.edit', $campaign)
            ->with('success', 'Recurring email created.');
    }

    // Renders an occurrence's report under the /recurring-email/{campaign}/... URL
    // namespace instead of /campaigns/{id}, so the address bar and breadcrumb
    // stay within the recurring context  reuses CampaignController::show()'s
    // exact rendering, no duplicated report logic.
    public function showOccurrence(Campaign $campaign, Campaign $occurrence, CampaignController $campaignController): Response
    {
        abort_unless($campaign->type === 'recurring', 404);
        abort_unless($occurrence->parent_id === $campaign->id, 404);

        return $campaignController->show($occurrence);
    }

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

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

        $smtpProfiles = SmtpConfig::orderByDesc('is_default')->orderBy('name')
            ->get(['id', 'name', 'from_name', 'from_email', 'is_default']);

        $occurrences = $campaign->occurrences()
            ->orderByDesc('created_at')
            ->paginate(20)
            ->through(fn(Campaign $o) => [
                'id'               => $o->id,
                'title'            => $o->title,
                'status'           => $o->status,
                'recipients_count' => $o->recipients_count,
                'emails_sent'      => $o->emails_sent,
                'emails_failed'    => $o->emails_failed,
                'emails_bounced'   => $o->emails_bounced,
                'unique_opens'     => $o->unique_opens,
                'unique_clicks'    => $o->unique_clicks,
                'created_at'       => $o->created_at->toISOString(),
            ]);

        return Inertia::render('Recurring/Edit', [
            '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' => []],
                'frequency'          => $campaign->frequency,
                'recurring_days'     => $campaign->recurring_days ?? [],
                'recurring_time'     => $campaign->recurring_time,
                'recurring_timezone' => $campaign->recurring_timezone,
                'auto_send'          => $campaign->auto_send,
                'start_date'         => $campaign->start_date?->toDateString(),
                'end_type'           => $campaign->end_type,
                'end_date'           => $campaign->end_date?->toDateString(),
                'end_count'          => $campaign->end_count,
                'occurrences_sent'   => $campaign->occurrences_sent,
                'last_sent_at'       => $campaign->last_sent_at?->toISOString(),
                'next_run_at'        => $campaign->next_run_at?->toISOString(),
            ],
            'lists'           => $lists,
            'tags'            => $tags,
            'occurrences'     => $occurrences,
            'smtpProfiles'    => $smtpProfiles,
            '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(),
            'serverNow'       => now()->toISOString(),
        ]);
    }

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

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

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

        if ($request->has('settings')) {
            $current          = $campaign->settings ?? [];
            $data['settings'] = array_merge($current, (array) $request->input('settings'));
        }

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

        if (collect(self::SCHEDULE_FIELDS)->intersect(array_keys($data))->isNotEmpty()) {
            $campaign->update([
                'next_run_at' => RecurringScheduleCalculator::nextRunAt($campaign->fresh()),
            ]);
        }

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

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

        $data = $request->validate([
            'status'             => 'required|in:draft,active,paused',
            'recurring_timezone' => 'nullable|string|max:64',
        ]);

        $campaign->status = $data['status'];

        // Belt-and-suspenders: if the caller has a fresher browser timezone
        // than what's stored, apply it before recomputing next_run_at so
        // activation is never computed against a stale/default timezone.
        if (!empty($data['recurring_timezone'])) {
            $campaign->recurring_timezone = $data['recurring_timezone'];
        }

        if ($data['status'] === 'active') {
            $campaign->next_run_at = RecurringScheduleCalculator::nextRunAt($campaign);
        }

        $campaign->save();

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

    public function sendTest(Request $request, Campaign $campaign, EmailRenderer $renderer): JsonResponse
    {
        abort_unless($campaign->type === 'recurring', 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 {
            $smtp = SmtpConfigService::resolveForCampaign($campaign, $request->input('smtp_config_id'));
            if ($smtp) {
                SmtpConfigService::configureRuntime($smtp);
            }

            $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 countRecipients(Request $request): JsonResponse
    {
        $config = $request->all();

        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())]);
    }

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

        Campaign::where('parent_id', $campaign->id)->delete();
        $campaign->delete();

        return redirect()->route('recurring.index')->with('success', 'Recurring email deleted.');
    }

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

        $templateIds = Campaign::whereIn('id', $data['ids'])->where('type', 'recurring')->pluck('id');

        Campaign::whereIn('parent_id', $templateIds)->delete();
        $count = Campaign::whereIn('id', $templateIds)->delete();

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