<?php

namespace App\Http\Controllers;

use App\Models\Campaign;
use App\Models\ContactList;
use App\Models\SmtpConfig;
use App\Models\Tag;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use Illuminate\Support\Str;

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

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

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

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

        $sequenceIds = $sequences->pluck('id');
        $eventStats = \App\Models\CampaignEvent::whereIn('campaign_id', $sequenceIds)
            ->selectRaw('
                campaign_id,
                count(distinct case when event_type = "enrolled" then contact_id end) as enrolled_count,
                count(distinct case when event_type = "sent" then contact_id end) as unique_recipients,
                count(distinct case when event_type = "open" then contact_id end) as unique_opens,
                count(distinct case when event_type = "click" then contact_id end) as unique_clicks
            ')
            ->groupBy('campaign_id')
            ->get()
            ->keyBy('campaign_id');

        return Inertia::render('Sequences/Index', [
            'sequences' => $sequences->through(function ($s) use ($eventStats) {
                $st = $eventStats->get($s->id);
                $uniqueRecipients = $st?->unique_recipients ?? 0;
                $uniqueOpens      = $st?->unique_opens ?? 0;
                $uniqueClicks     = $st?->unique_clicks ?? 0;
                $enrolledCount    = $st?->enrolled_count ?? 0;

                return [
                    'id'          => $s->id,
                    'title'       => $s->title,
                    'status'      => $s->status,
                    'emails_count'=> count($s->settings['steps'] ?? []),
                    'subscribers' => $enrolledCount > 0 ? $enrolledCount : ($s->recipients_count ?? 0),
                    'avg_open'    => $uniqueRecipients > 0 ? round(($uniqueOpens / $uniqueRecipients) * 100, 1) : 0,
                    'avg_click'   => $uniqueRecipients > 0 ? round(($uniqueClicks / $uniqueRecipients) * 100, 1) : 0,
                    'has_sent'    => $uniqueRecipients > 0,
                    'created_at'  => $s->created_at->format('M d, Y'),
                    'settings'    => $s->settings,
                ];
            }),
            'filters' => $request->only(['search', 'status']),
        ]);
    }

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

        $sequence = Campaign::create([
            'title'      => $data['title'],
            'type'       => 'sequence',
            'status'     => 'draft',
            'created_by' => auth()->id(),
            'recipients_config' => [
                'mode'     => 'list_tag',
                'included' => [['list_id' => null, 'tag_id' => null]],
                'excluded' => [],
            ],
            'settings'   => [
                'timezone' => 'Contact\'s local time',
                'smtp_config_id' => null,
                'completion_tags' => ['Onboarded'],
                'description' => 'Onboards new subscribers.',
                'steps' => [],
            ],
        ]);

        return redirect()->route('sequences.edit', $sequence)
            ->with('success', 'Sequence created.');
    }

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

        $lists = ContactList::orderBy('title')->get(['id', 'title']);
        $tags  = Tag::orderBy('title')->get(['id', 'title']);
        $defaultSmtp = SmtpConfig::where('is_default', true)->first();
        $smtpConfigs = SmtpConfig::orderBy('name')->get(['id', 'name', 'from_name', 'from_email', 'is_default']);
        $campaigns   = Campaign::where('user_id', $sequence->user_id)->whereIn('type', ['campaign', 'recurring'])->orderByDesc('id')->get(['id', 'title']);
        $automations = Campaign::where('user_id', $sequence->user_id)->whereIn('type', ['automation', 'sequence'])->orderByDesc('id')->get(['id', 'title', 'type']);

        $nodeEvents = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->whereNotNull('node_id')
            ->with(['contact' => fn($q) => $q->withoutGlobalScopes()->select(['id', 'email', 'first_name', 'last_name'])])
            ->orderByDesc('id')
            ->get()
            ->map(fn($e) => [
                'id'          => $e->id,
                'node_id'     => $e->node_id,
                'event_type'  => $e->event_type,
                'reason'      => $e->reason,
                'contact'     => $e->contact ? [
                    'id'    => $e->contact->id,
                    'email' => $e->contact->email,
                    'name'  => trim($e->contact->first_name . ' ' . $e->contact->last_name),
                ] : null,
                'created_at'  => $e->created_at->toISOString(),
            ]);

        $enrolledCount = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'enrolled')
            ->distinct('contact_id')
            ->count();

        $completedCount = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'completed')
            ->distinct('contact_id')
            ->count();

        $uniqueOpens = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'open')
            ->distinct('contact_id')
            ->count();

        $uniqueRecipients = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'sent')
            ->distinct('contact_id')
            ->count();

        $avgOpen = $uniqueRecipients > 0 ? round(($uniqueOpens / $uniqueRecipients) * 100) : 0;

        $steps = $sequence->settings['steps'] ?? [];
        $stepStats = $this->computeStepStats($sequence->id, $steps);

        $pendingJobs = [];
        try {
            $rawJobs = \Illuminate\Support\Facades\DB::table('jobs')->get();
            foreach ($rawJobs as $job) {
                $payload = json_decode($job->payload, true);
                $commandName = $payload['data']['commandName'] ?? '';
                if ($commandName === \App\Jobs\ProcessSequenceStepJob::class) {
                    $command = unserialize($payload['data']['command']);
                    if ($command && $command->sequenceId === $sequence->id) {
                        $contact = \App\Models\Contact::withoutGlobalScopes()->find($command->contactId);

                        $step = collect($steps)->firstWhere('id', $command->stepId ?? null);
                        $stepDesc = $step ? 'Email: ' . ($step['subject'] ?? '(Untitled Email)') : 'Processing Step';

                        $availableAt = \Illuminate\Support\Carbon::createFromTimestamp($job->available_at);
                        $diff = $availableAt->isPast() ? 'Pending execution' : 'Runs ' . $availableAt->diffForHumans(null, false, false, 2);

                        $pendingJobs[] = [
                            'id' => $job->id,
                            'contact_email' => $contact?->email ?? 'Unknown contact',
                            'run_time' => $diff,
                            'step_desc' => $stepDesc,
                            'available_at' => $availableAt->toISOString(),
                        ];
                    }
                }
            }
        } catch (\Exception $e) {
            // Table 'jobs' might not exist in some setups
        }

        $failedJobs = [];
        try {
            $rawFailed = \Illuminate\Support\Facades\DB::table('failed_jobs')->orderByDesc('failed_at')->get();
            foreach ($rawFailed as $job) {
                $payload = json_decode($job->payload, true);
                $commandName = $payload['data']['commandName'] ?? '';
                if ($commandName === \App\Jobs\ProcessSequenceStepJob::class) {
                    $command = unserialize($payload['data']['command']);
                    if ($command && $command->sequenceId === $sequence->id) {
                        $contact = \App\Models\Contact::withoutGlobalScopes()->find($command->contactId);
                        $step = collect($steps)->firstWhere('id', $command->stepId ?? null);

                        $failedJobs[] = [
                            'id' => $job->id,
                            'contact_email' => $contact?->email ?? 'Unknown contact',
                            'step_desc' => $step ? 'Email: ' . ($step['subject'] ?? '(Untitled Email)') : 'Unknown step',
                            'failed_at' => \Illuminate\Support\Carbon::parse($job->failed_at)->toISOString(),
                            'exception' => Str::limit(explode("\n", $job->exception)[0] ?? 'Unknown error', 160),
                        ];
                    }
                }
            }
        } catch (\Exception $e) {
            // Table 'failed_jobs' might not exist in some setups
        }

        return Inertia::render('Sequences/Create', [
            'sequence' => [
                'id'       => $sequence->id,
                'title'    => $sequence->title,
                'status'   => $sequence->status,
                'recipients_config' => $sequence->recipients_config ?? [
                    'mode'     => 'list_tag',
                    'included' => [['list_id' => null, 'tag_id' => null]],
                    'excluded' => [],
                ],
                'settings' => $sequence->settings ?? [
                    'timezone' => 'Contact\'s local time',
                    'smtp_config_id' => null,
                    'completion_tags' => [],
                    'description' => '',
                    'steps' => [],
                ],
                'node_events' => $nodeEvents,
            ],
            'stats' => [
                'enrolled'  => $enrolledCount,
                'completed' => $completedCount,
                'avgOpen'   => $avgOpen,
            ],
            'stepStats' => $stepStats,
            'pendingJobs' => $pendingJobs,
            'failedJobs' => $failedJobs,
            'queueConnection' => config('queue.default'),
            'lists' => $lists,
            'tags'  => $tags,
            'smtpConfigs' => $smtpConfigs,
            'filterFieldMenu' => \App\Services\AdvancedFilter\FilterFieldRegistry::menuTree(),
            'campaigns'   => $campaigns,
            'automations' => $automations,
            'defaultSender' => $defaultSmtp ? [
                'profile'    => $defaultSmtp->name,
                'from_name'  => $defaultSmtp->from_name,
                'from_email' => $defaultSmtp->from_email,
            ] : null,
        ]);
    }

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

        $data = $request->validate([
            'title'              => 'sometimes|string|max:192',
            'status'             => 'sometimes|string|in:draft,active,paused,completed',
            'settings'           => 'sometimes|array',
            'recipients_config'  => 'sometimes|array',
        ]);

        if (isset($data['title'])) {
            $sequence->title = $data['title'];
        }

        if (isset($data['status'])) {
            $oldStatus = $sequence->status;
            $sequence->status = $data['status'];
            if ($data['status'] === 'active' && $oldStatus !== 'active') {
                $this->resumePausedJobs($sequence);
            }
        }

        if (isset($data['settings'])) {
            $sequence->settings = $data['settings'];
        }

        if (isset($data['recipients_config'])) {
            $sequence->recipients_config = $data['recipients_config'];
        }

        $sequence->save();

        return response()->json([
            'success' => true,
            'sequence' => [
                'id'                 => $sequence->id,
                'title'              => $sequence->title,
                'status'             => $sequence->status,
                'settings'           => $sequence->settings,
                'recipients_config'  => $sequence->recipients_config,
            ]
        ]);
    }

    public function show(Request $request, Campaign $sequence): Response
    {
        abort_unless($sequence->type === 'sequence', 404);

        // Steps predating the sequence revamp may still contain non-email
        // node types (condition/tag) with no `subject`; the report only
        // covers email steps, so those legacy entries are filtered out here.
        $steps = array_values(array_filter($sequence->settings['steps'] ?? [], fn($s) => ($s['type'] ?? 'email') === 'email'));
        $stepStats = $this->computeStepStats($sequence->id, $steps);

        // "View Report" on an individual step narrows every event tab
        // (deliverability/opens/clicks/...) down to just that step's events.
        $stepFilterId = $request->input('step');
        $filteredStep = $stepFilterId
            ? collect($steps)->firstWhere('id', $stepFilterId)
            : null;
        [$firstSentAt, $nextScheduledAt] = $this->stepSendTimes($sequence);

        // Keyed by step id, so every event row (deliveries/opens/clicks/...)
        // can say which step it belongs to - the same contact appears once
        // per step they've reached, so without this the tables are
        // indistinguishable duplicate-looking rows for the same address.
        $stepMeta = [];
        foreach ($steps as $idx => $step) {
            $stepMeta[$step['id']] = ['step_number' => $idx + 1, 'subject' => $step['subject'] ?? '(Untitled Email)'];
        }

        $emailList = collect($steps)->values()->map(function ($step, $idx) use ($stepStats, $firstSentAt, $nextScheduledAt) {
            $st = $stepStats[$step['id']] ?? ['sent' => 0, 'opens' => 0, 'clicks' => 0];

            return [
                'id'                 => $step['id'],
                'step_number'        => $idx + 1,
                'subject'            => $step['subject'] ?? '(Untitled Email)',
                'delay'              => $step['delay'] ?? null,
                'sent'               => $st['sent'],
                'opens'              => $st['opens'],
                'clicks'             => $st['clicks'],
                'open_rate'          => $st['sent'] > 0 ? round(($st['opens'] / $st['sent']) * 100, 1) : 0,
                'click_rate'         => $st['sent'] > 0 ? round(($st['clicks'] / $st['sent']) * 100, 1) : 0,
                'first_sent_at'      => $firstSentAt[$step['id']] ?? null,
                'next_scheduled_at'  => $nextScheduledAt[$step['id']] ?? null,
            ];
        });

        $enrolledCount = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'enrolled')->distinct('contact_id')->count('contact_id');
        $completedCount = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'completed')->distinct('contact_id')->count('contact_id');
        $uniqueRecipients = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'sent')->distinct('contact_id')->count('contact_id');
        $uniqueOpens = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'open')->distinct('contact_id')->count('contact_id');
        $uniqueClicks = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'click')->distinct('contact_id')->count('contact_id');
        $unsubscribesCount = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'unsubscribe')->distinct('contact_id')->count('contact_id');

        return Inertia::render('Sequences/Show', [
            'sequence' => [
                'id'                 => $sequence->id,
                'title'              => $sequence->title,
                'status'             => $sequence->status,
                'recipients_count'   => $sequence->recipients_count ?? 0,
                'enrolled_count'     => $enrolledCount,
                'completed_count'    => $completedCount,
                'emails_sent'        => $sequence->emails_sent ?? 0,
                'unique_recipients'  => $uniqueRecipients,
                'unique_opens'       => $uniqueOpens,
                'unique_clicks'      => $uniqueClicks,
                'unsubscribes_count' => $unsubscribesCount,
                'created_at'         => $sequence->created_at->toISOString(),
                'updated_at'         => $sequence->updated_at->toISOString(),
            ],
            'emailList'    => $emailList,
            'stepFilter'   => $filteredStep ? [
                'id'      => $filteredStep['id'],
                'subject' => $filteredStep['subject'] ?? '(Untitled Email)',
            ] : null,
            'deliveries'   => $this->sequenceEventPage($sequence->id, 'sent', 'deliveries_page', $stepMeta, $filteredStep['id'] ?? null),
            'opens'        => $this->sequenceEventPage($sequence->id, 'open', 'opens_page', $stepMeta, $filteredStep['id'] ?? null),
            'clicks'       => $this->sequenceEventPage($sequence->id, 'click', 'clicks_page', $stepMeta, $filteredStep['id'] ?? null),
            'unsubscribes' => $this->sequenceEventPage($sequence->id, 'unsubscribe', 'unsubscribes_page', $stepMeta, $filteredStep['id'] ?? null),
            'bounces'      => $this->sequenceEventPage($sequence->id, 'bounced', 'bounces_page', $stepMeta, $filteredStep['id'] ?? null),
        ]);
    }

    private function sequenceEventPage(int $sequenceId, string $type, string $pageName, array $stepMeta, ?string $nodeId = null)
    {
        return \App\Models\CampaignEvent::with('contact:id,email,first_name,last_name,status')
            ->where('campaign_id', $sequenceId)
            ->where('event_type', $type)
            ->when($nodeId, fn($q) => $q->where('node_id', $nodeId))
            ->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,
                'step_number' => $stepMeta[$e->node_id]['step_number'] ?? null,
                'step_subject'=> $stepMeta[$e->node_id]['subject'] ?? null,
                'at'          => ($type === 'open' ? $e->updated_at : $e->created_at)->toISOString(),
            ]);
    }

    /**
     * Per-step sent/opens/clicks, keyed by step id - shared by the builder's
     * live diagnostics (edit()) and the report page (show()).
     */
    private function computeStepStats(int $sequenceId, array $steps): array
    {
        $raw = \App\Models\CampaignEvent::where('campaign_id', $sequenceId)
            ->whereNotNull('node_id')
            ->whereIn('event_type', ['sent', 'open', 'click'])
            ->selectRaw('
                node_id,
                count(distinct case when event_type = "sent" then contact_id end) as sent,
                count(distinct case when event_type = "open" then contact_id end) as opens,
                count(distinct case when event_type = "click" then contact_id end) as clicks
            ')
            ->groupBy('node_id')
            ->get()
            ->keyBy('node_id');

        $stats = [];
        foreach ($steps as $step) {
            $st = $raw->get($step['id']);
            $stats[$step['id']] = [
                'sent'   => (int) ($st->sent ?? 0),
                'opens'  => (int) ($st->opens ?? 0),
                'clicks' => (int) ($st->clicks ?? 0),
            ];
        }

        return $stats;
    }

    /**
     * For each step (keyed by step id): the earliest timestamp it was
     * actually sent at (if any), and - failing that - the earliest
     * already-queued dispatch time for that step still pending in the
     * `jobs` table (an exact, real scheduled time, not a theoretical one,
     * since per-contact delays run from each contact's own enrollment time).
     *
     * @return array{0: array<string,string>, 1: array<string,string>}
     */
    private function stepSendTimes(Campaign $sequence): array
    {
        $firstSentAt = \App\Models\CampaignEvent::where('campaign_id', $sequence->id)
            ->where('event_type', 'sent')
            ->whereNotNull('node_id')
            ->selectRaw('node_id, MIN(created_at) as first_sent_at')
            ->groupBy('node_id')
            ->pluck('first_sent_at', 'node_id')
            ->map(fn($v) => \Illuminate\Support\Carbon::parse($v)->toISOString())
            ->all();

        $nextScheduledAt = [];

        try {
            $jobs = \Illuminate\Support\Facades\DB::table('jobs')->get();
            foreach ($jobs as $job) {
                $payload = json_decode($job->payload, true);
                if (($payload['data']['commandName'] ?? '') !== \App\Jobs\ProcessSequenceStepJob::class) {
                    continue;
                }
                $command = unserialize($payload['data']['command']);
                if (!$command || $command->sequenceId !== $sequence->id) {
                    continue;
                }
                $stepId = $command->stepId ?? null;
                if (!$stepId) {
                    continue;
                }
                if (!isset($nextScheduledAt[$stepId]) || $job->available_at < $nextScheduledAt[$stepId]) {
                    $nextScheduledAt[$stepId] = $job->available_at;
                }
            }
        } catch (\Exception $e) {
            // Table 'jobs' might not exist in some setups
        }

        $nextScheduledAt = array_map(
            fn($timestamp) => \Illuminate\Support\Carbon::createFromTimestamp($timestamp)->toISOString(),
            $nextScheduledAt
        );

        return [$firstSentAt, $nextScheduledAt];
    }

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

        return redirect()->route('sequences.index')
            ->with('success', 'Sequence deleted.');
    }

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

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

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

        $copy->save();

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

    public function bulkDestroy(Request $request): JsonResponse
    {
        $ids = $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'integer',
        ])['ids'];

        Campaign::where('type', 'sequence')
            ->whereIn('id', $ids)
            ->delete();

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

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

        $request->validate([
            'email'   => 'required|email|max:255',
            'step_id' => 'required|string',
        ]);

        $steps = $sequence->settings['steps'] ?? [];
        $targetStep = $this->findStepById($steps, $request->input('step_id'));

        if (!$targetStep || $targetStep['type'] !== 'email') {
            return response()->json(['success' => false, 'message' => 'Step not found or not an email step.'], 404);
        }

        $blocks = $targetStep['blocks'] ?? [];

        if (!empty($targetStep['track_utm'])) {
            $renderer->setUtmData([
                'utm_source'   => $targetStep['utm_source'] ?? '',
                'utm_medium'   => $targetStep['utm_medium'] ?? 'email',
                'utm_campaign' => $targetStep['utm_campaign'] ?? '',
                'utm_term'     => $targetStep['utm_term'] ?? '',
                'utm_content'  => $targetStep['utm_content'] ?? '',
            ]);
        }

        $html = $renderer->render($blocks, [], $sequence->id, null, $targetStep['pre_header'] ?? null);

        try {
            $smtp = \App\Services\SmtpConfigService::resolveForCampaign($sequence);
            if ($smtp) {
                \App\Services\SmtpConfigService::configureRuntime($smtp);
            }

            $fromEmail = !empty($targetStep['from_email']) ? $targetStep['from_email'] : ($smtp?->from_email ?: null);
            $fromName  = !empty($targetStep['from_name']) ? $targetStep['from_name'] : ($smtp?->from_name ?: null);

            \Illuminate\Support\Facades\Mail::to($request->input('email'))
                ->send(new \App\Mail\CampaignTestMail(
                    $sequence,
                    $html,
                    $fromEmail,
                    $fromName,
                    $fromEmail,
                    $fromName
                ));
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
        }

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

    private function findStepById(array $steps, string $id): ?array
    {
        foreach ($steps as $step) {
            if (($step['id'] ?? null) === $id) {
                return $step;
            }
        }
        return null;
    }

    public function start(Request $request, Campaign $sequence)
    {
        abort_unless($sequence->type === 'sequence', 404);

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

        $contactIds = \App\Services\CampaignRecipientResolver::resolve($sequence->recipients_config, $sequence->user_id);

        $startDelaySeconds = 0;
        if ($data['send_mode'] === 'schedule') {
            $startDelaySeconds = max(0, (int) now()->diffInSeconds(\Carbon\Carbon::parse($data['scheduled_at']), false));
        }

        $sequence->update([
            'send_mode'    => $data['send_mode'],
            'scheduled_at' => $data['send_mode'] === 'schedule' ? $data['scheduled_at'] : null,
            'status'       => 'active',
        ]);

        $contacts = \App\Models\Contact::withoutGlobalScopes()->whereIn('id', $contactIds)->get();
        foreach ($contacts as $contact) {
            \App\Services\SequenceEngine::enroll($sequence, $contact, $startDelaySeconds);
        }

        $message = $data['send_mode'] === 'schedule'
            ? 'Sequence scheduled to start ' . \Carbon\Carbon::parse($data['scheduled_at'])->format('M j, Y g:i A') . '.'
            : "Sequence started for {$contacts->count()} recipient(s).";

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

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

        $contactIds = \App\Services\CampaignRecipientResolver::resolve($sequence->recipients_config, $sequence->user_id);
        $contacts = \App\Models\Contact::withoutGlobalScopes()->whereIn('id', $contactIds)->get()->unique('email');
        $count = 0;

        foreach ($contacts as $contact) {
            \App\Services\SequenceEngine::enroll($sequence, $contact);
            $count++;
        }

        return back()->with('success', "Successfully enrolled {$count} matching contact(s).");
    }

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

        // Cancel and delete pending jobs from queue
        try {
            $jobs = \Illuminate\Support\Facades\DB::table('jobs')->get();
            foreach ($jobs as $job) {
                $payload = json_decode($job->payload, true);
                $commandName = $payload['data']['commandName'] ?? '';
                if ($commandName === \App\Jobs\ProcessSequenceStepJob::class) {
                    $command = unserialize($payload['data']['command']);
                    if ($command && $command->sequenceId === $sequence->id) {
                        \Illuminate\Support\Facades\DB::table('jobs')->where('id', $job->id)->delete();
                    }
                }
            }
        } catch (\Exception $e) {
            \Illuminate\Support\Facades\Log::error("Failed to cancel queued jobs on sequence reset: " . $e->getMessage());
        }

        \App\Models\CampaignEvent::where('campaign_id', $sequence->id)->delete();
        $sequence->update(['recipients_count' => 0, 'status' => 'active']);

        return $this->enrollAll($sequence);
    }

    public function previewStep(Request $request, Campaign $sequence, \App\Services\EmailRenderer $renderer): \Illuminate\Http\JsonResponse
    {
        abort_unless($sequence->type === 'sequence', 404);

        $request->validate([
            'step_id' => 'required|string',
        ]);

        $steps = $sequence->settings['steps'] ?? [];
        $targetStep = $this->findStepById($steps, $request->input('step_id'));

        if (!$targetStep || $targetStep['type'] !== 'email') {
            return response()->json(['html' => '<div style="padding: 20px; text-align: center; color: #64748b;">No email preview available for this step type.</div>']);
        }

        $blocks = $targetStep['blocks'] ?? [];
        $html = $renderer->render($blocks, [], $sequence->id, null, $targetStep['pre_header'] ?? null);

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

    private function resumePausedJobs(Campaign $sequence): void
    {
        try {
            $jobs = \Illuminate\Support\Facades\DB::table('jobs')->get();
            foreach ($jobs as $job) {
                $payload = json_decode($job->payload, true);
                $commandName = $payload['data']['commandName'] ?? '';
                if ($commandName === \App\Jobs\ProcessSequenceStepJob::class) {
                    $command = unserialize($payload['data']['command']);
                    if ($command && isset($command->sequenceId) && $command->sequenceId === $sequence->id) {
                        if (!empty($command->isPausedRelease)) {
                            // Reset the paused flag
                            $command->isPausedRelease = false;
                            
                            \Illuminate\Support\Facades\DB::table('jobs')
                                ->where('id', $job->id)
                                ->update([
                                    'available_at' => time(),
                                    'payload' => json_encode(array_merge($payload, [
                                        'data' => array_merge($payload['data'], [
                                            'command' => serialize($command)
                                        ])
                                    ]))
                                ]);
                        }
                    }
                }
            }
        } catch (\Exception $e) {
            \Illuminate\Support\Facades\Log::error("Failed to resume paused jobs: " . $e->getMessage());
        }
    }

}
