<?php

namespace App\Jobs\Contact;

use App\Actions\Communications\DispatchContactMessageAction;
use App\Enum\Contact\BulkEmailCampaignStatus;
use App\Enum\Contact\MessageOrigin;
use App\Models\Client\Client;
use App\Models\Contact\BulkEmailCampaign;
use App\Models\Contact\ContactTemplate;
use App\Models\Solicitor\Solicitor;
use App\Services\Client\ClientFilter;
use App\ValueObjects\Communications\DispatchRequest;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Throwable;

/**
 * Fans a single email template out to every client matching a stored filter.
 *
 * Reuses the per-recipient pipeline (DispatchContactMessageAction → SendContactMessageJob)
 * so each client still gets full sender resolution, suppression skipping, rate limiting,
 * placeholder rendering, and its own delivery job.
 */
class ProcessBulkEmailCampaignJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /** A whole-campaign retry would double-count recipients — surface failure on the row instead. */
    public int $tries = 1;
    public int $timeout = 3600;

    public function __construct(public int $campaignId) {}

    public function handle(DispatchContactMessageAction $dispatch, ClientFilter $filter): void
    {
        $campaign = BulkEmailCampaign::withoutGlobalScopes()->find($this->campaignId);
        if (!$campaign || $campaign->status !== BulkEmailCampaignStatus::PENDING) {
            return;
        }

        // The `solicitor` global scope is skipped in console/queue context
        // (BelongsToSolicitor::bootBelongsToSolicitor returns early), so resolve and bind
        // the tenant explicitly — the dispatch action calls app(Solicitor::class) internally
        // for sender resolution and suppression checks.
        $solicitor = Solicitor::query()->find($campaign->solicitor_id);
        if (!$solicitor) {
            $this->fail($campaign, 'Solicitor not found.');
            return;
        }
        app()->instance(Solicitor::class, $solicitor);

        $template = ContactTemplate::withoutGlobalScopes()
            ->where('solicitor_id', $campaign->solicitor_id)
            ->find($campaign->template_id);

        if (!$template || !$template->is_active || !$template->isEmail() || !$template->allow_manual_send) {
            $this->fail($campaign, 'Template is missing, inactive, not an email template, or does not allow manual sending.');
            return;
        }

        $campaign->update([
            'status' => BulkEmailCampaignStatus::PROCESSING,
            'started_at' => now(),
        ]);

        try {
            $query = $filter->apply(Client::query(), $campaign->filters ?? []);
            $query = $filter->applyTenant($query, (int) $campaign->solicitor_id);

            $query->chunkById(200, function ($clients) use ($dispatch, $campaign, $template) {
                foreach ($clients as $client) {
                    $request = new DispatchRequest(
                        messageable: $client,
                        template: $template,
                        user: $campaign->creator,
                        senderEmailId: $campaign->sender_email_id,
                        origin: MessageOrigin::MANUAL,
                        originDetail: ['bulk_campaign_id' => $campaign->id],
                        extraContext: [
                            'document_client_id' => $client->id,
                            'document_client_morph' => $client->getMorphClass(),
                        ],
                    );

                    try {
                        $campaign->incrementResult($dispatch($request));
                    } catch (Throwable $e) {
                        $campaign->increment('failed_count');
                        $campaign->increment('processed_count');
                        Log::warning('Bulk email dispatch failed for client', [
                            'campaign_id' => $campaign->id,
                            'client_id' => $client->id,
                            'error' => $e->getMessage(),
                        ]);
                    }
                }
            });

            $campaign->update([
                'status' => BulkEmailCampaignStatus::COMPLETED,
                'completed_at' => now(),
            ]);
        } catch (Throwable $e) {
            $this->fail($campaign, $e->getMessage());
            throw $e;
        }
    }

    public function failed(?Throwable $e): void
    {
        $campaign = BulkEmailCampaign::withoutGlobalScopes()->find($this->campaignId);
        if ($campaign && !$campaign->status->isFinished()) {
            $this->fail($campaign, $e?->getMessage() ?? 'Job failed.');
        }
    }

    private function fail(BulkEmailCampaign $campaign, string $reason): void
    {
        $campaign->update([
            'status' => BulkEmailCampaignStatus::FAILED,
            'last_error' => $reason,
            'completed_at' => now(),
        ]);
    }
}
