<?php

namespace Modules\WhatsApp\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Modules\WhatsApp\Entities\Services\TransportManager;
use Modules\WhatsApp\Entities\Services\WaSender;
use Modules\WhatsApp\Entities\WaMessage;

/**
 * Delivers one outbound WhatsApp message.
 *
 * Designed so a message is NEVER silently lost:
 *  - transient problems (channel offline, network, rate limit) → the message is
 *    held as 'pending' with a backoff and the job re-dispatches itself;
 *  - permanent problems (bad number/template) → 'failed' with the reason;
 *  - the SweepPendingMessages cron is the belt-and-braces safety net that
 *    re-drives anything still pending (e.g. after a worker outage).
 */
class SendWhatsAppMessage implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $messageId;

    // We manage our own retry/backoff via the message row, so one attempt here.
    public int $tries = 1;
    public int $timeout = 120;

    public function __construct(int $messageId)
    {
        $this->messageId = $messageId;
    }

    public function handle(TransportManager $manager, WaSender $sender): void
    {
        /** @var WaMessage|null $message */
        $message = WaMessage::find($this->messageId);
        if (!$message || $message->direction !== 'out') {
            return;
        }

        // Already delivered / failed / handed to the bridge — nothing to do.
        if (!in_array($message->status, ['pending', 'queued'])) {
            return;
        }

        // Throttle gate: not yet its turn → re-dispatch at the right time.
        if ($message->available_at && $message->available_at->isFuture()) {
            $sender->dispatchFor($message);
            return;
        }

        // Resolve the channel (it may have been created/attached after enqueue).
        $channel = $message->channel;
        if (!$channel) {
            $channel = $sender->resolveChannel($message->business_id, []);
            if ($channel) {
                $message->forceFill(['channel_id' => $channel->id])->save();
            }
        }

        if (!$channel) {
            // No number configured yet — keep it, retry later, alert nobody-drops.
            $this->hold($message, $sender, 'No WhatsApp channel configured for this business.');
            return;
        }

        $message->increment('attempts');

        try {
            $result = $manager->driver($channel)->send($channel, $message);
        } catch (\Throwable $e) {
            \Log::error('WhatsApp send error: ' . $e->getMessage());
            $this->hold($message, $sender, 'Unexpected error: ' . $e->getMessage());
            return;
        }

        if ($result->success) {
            $message->markSent($result->providerMessageId);
            return;
        }

        if ($result->handedOff) {
            // Bridge owns it now; final status arrives via /agent/ack.
            return;
        }

        if ($result->shouldRetry) {
            $this->hold($message, $sender, $result->error ?? 'Temporary delivery problem.');
            return;
        }

        $message->markFailed($result->error ?? 'Delivery failed.');
    }

    /** Hold for retry unless a (configured) hard attempt ceiling is reached. */
    private function hold(WaMessage $message, WaSender $sender, string $reason): void
    {
        $maxAttempts = (int) config('whatsapp.retry.max_attempts', 0);
        if ($maxAttempts > 0 && $message->attempts >= $maxAttempts) {
            $message->markFailed("Gave up after {$message->attempts} attempts: {$reason}");
            return;
        }

        $backoff = (int) config('whatsapp.retry.backoff', 60);
        $message->holdForRetry($reason, $backoff);
        $sender->dispatchFor($message);
    }

    /** If the queue itself fails the job, keep the message recoverable. */
    public function failed(\Throwable $e): void
    {
        $message = WaMessage::find($this->messageId);
        if ($message && in_array($message->status, ['pending', 'queued'])) {
            $message->holdForRetry('Job failure: ' . $e->getMessage(), (int) config('whatsapp.retry.backoff', 60));
        }
    }
}
