<?php

namespace App\Jobs;

use App\Models\Campaign;
use App\Services\CampaignRecipientResolver;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class ProcessCampaignJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 3600; // 1 hour max for large lists

    public function __construct(public readonly Campaign $campaign) {}

    public function handle(): void
    {
        $campaign = $this->campaign->fresh();

        if (!$campaign || !in_array($campaign->status, ['queued', 'scheduled'])) {
            return;
        }

        $contactIds = $this->resolveRecipients($campaign);
        $total      = count($contactIds);

        if ($total === 0) {
            $campaign->update(['status' => 'sent']);

            \App\Models\Notification::notify(
                $campaign->user_id,
                'info',
                'Campaign Completed (Empty)',
                'Your campaign "' . $campaign->title . '" completed processing but had 0 matching recipients.',
                '/campaigns/' . $campaign->id
            );

            return;
        }

        $campaign->update(['status' => 'sending', 'recipients_count' => $total]);

        $mode = $campaign->send_mode ?? 'now';

        foreach ($contactIds as $index => $contactId) {
            $delaySeconds = $this->computeDelay($campaign, $mode, $index, $total);

            SendCampaignEmailJob::dispatch($campaign->id, $contactId)
                ->delay(now()->addSeconds($delaySeconds));
        }

        // For range/schedule modes the individual jobs will run later;
        // for "now" they are queued with 0 delay.
        if ($mode !== 'now') {
            $campaign->update(['status' => 'scheduled']);
        }
    }

    private function computeDelay(Campaign $campaign, string $mode, int $index, int $total): int
    {
        return match ($mode) {
            'schedule' => max(0, (int) now()->diffInSeconds(
                Carbon::parse($campaign->scheduled_at),
                false
            )),
            'range' => $this->rangeDelay($campaign, $index, $total),
            default   => 0, // 'now'
        };
    }

    private function rangeDelay(Campaign $campaign, int $index, int $total): int
    {
        $start = Carbon::parse($campaign->range_start);
        $end   = Carbon::parse($campaign->range_end);

        $startOffset = max(0, (int) now()->diffInSeconds($start, false));
        $span        = max(1, $end->diffInSeconds($start));
        $slotOffset  = $total > 1 ? (int) ($span / ($total - 1) * $index) : 0;

        return $startOffset + $slotOffset;
    }

    private function resolveRecipients(Campaign $campaign): array
    {
        return CampaignRecipientResolver::resolve($campaign->recipients_config, $campaign->user_id);
    }
}
