<?php

namespace App\Jobs;

use App\Models\ChannelIntegration;
use App\Models\Conversation;
use App\Models\EmailAccount;
use App\Models\Message;
use App\Services\Channels\LiveChatService;
use App\Services\Channels\SlackService;
use App\Services\Channels\TelegramService;
use App\Services\Channels\TwilioSMSService;
use App\Services\Channels\WhatsAppService;
use App\Services\Email\EmailSendService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

/**
 * Delivers an AI-generated reply over the conversation's channel.
 *
 * GenerateAIReplyJob writes the `messages` row with ai_status='approved'.
 * THIS job picks it up and actually puts the bytes on the wire — SMTP for
 * email, Twilio for SMS, Graph API for WhatsApp, Bot API for Telegram,
 * chat.postMessage for Slack. The widget channel never reaches this job
 * because the messages row IS the delivery (visitor polls /history).
 *
 * Routes purely by `conversation.channel`, mirroring the per-channel
 * delivery helpers in ReplyComposer. Marks the message delivery_status as
 * 'sent' / 'failed' so the inbox and analytics reflect the real outcome.
 */
class SendAIReplyJob implements ShouldQueue, ShouldBeUnique
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public array $backoff = [30, 120, 300];
    public int $timeout = 60;
    public int $uniqueFor = 300;

    public function __construct(
        public readonly Message $message,
    ) {}

    public function uniqueId(): string
    {
        return 'send-ai-reply-' . $this->message->id;
    }

    public function handle(): void
    {
        $message = $this->message->fresh();
        if (!$message) {
            return;
        }

        // Idempotency: skip if a prior attempt already marked this sent.
        if (in_array($message->delivery_status, ['sent', 'delivered'], true)) {
            return;
        }

        $conversation = $message->conversation;
        if (!$conversation) {
            $this->fail($message, 'Conversation not found');
            return;
        }

        $channel = $conversation->channel ?? 'email';

        try {
            match ($channel) {
                'email'            => $this->deliverEmail($message, $conversation),
                'sms'              => $this->deliverSms($message, $conversation),
                'whatsapp'         => $this->deliverWhatsApp($message, $conversation),
                'telegram'         => $this->deliverTelegram($message, $conversation),
                'slack'            => $this->deliverSlack($message, $conversation),
                'live_chat', 'chat' => $this->deliverLiveChat($message, $conversation),
                default            => $this->fail($message, "Unsupported channel: {$channel}"),
            };
        } catch (\Throwable $e) {
            Log::error('SendAIReplyJob: delivery failed', [
                'message_id' => $message->id,
                'channel' => $channel,
                'error' => $e->getMessage(),
            ]);
            $this->fail($message, $e->getMessage());
            throw $e;
        }
    }

    protected function deliverEmail(Message $message, Conversation $conversation): void
    {
        $emailAccount = $conversation->emailAccount
            ?? EmailAccount::where('workspace_id', $conversation->workspace_id)
                ->where('status', 'connected')
                ->first();

        if (!$emailAccount) {
            $this->fail($message, 'No email account connected');
            return;
        }

        app(EmailSendService::class)->sendReply($message, $emailAccount);
        // sendReply() flips delivery_status to 'sent' on success.
    }

    protected function deliverSms(Message $message, Conversation $conversation): void
    {
        $integration = $this->integration($conversation->workspace_id, 'sms');
        if (!$integration) {
            $this->fail($message, 'SMS not connected');
            return;
        }

        $toPhone = $conversation->contact?->phone;
        if (!$toPhone) {
            $this->fail($message, 'No phone number for contact');
            return;
        }

        $creds = $integration->credentials ?? [];
        $result = app(TwilioSMSService::class)->sendSMS(
            $toPhone,
            $message->body_text ?? '',
            $creds['phone_number'] ?? null,
            $creds['sid'] ?? null,
            $creds['auth_token'] ?? null,
        );

        $message->update([
            'delivery_status' => 'sent',
            'sent_at' => now(),
            'channel_message_id' => $result->sid ?? null,
        ]);
    }

    protected function deliverWhatsApp(Message $message, Conversation $conversation): void
    {
        $integration = $this->integration($conversation->workspace_id, 'whatsapp');
        if (!$integration) {
            $this->fail($message, 'WhatsApp not connected');
            return;
        }

        $toPhone = $conversation->contact?->phone;
        if (!$toPhone) {
            $this->fail($message, 'No phone number for contact');
            return;
        }

        $creds = $integration->credentials ?? [];
        $result = app(WhatsAppService::class)->sendText(
            $creds['phone_number_id'] ?? '',
            $creds['access_token'] ?? '',
            $toPhone,
            $message->body_text ?? '',
        );

        $message->update([
            'delivery_status' => 'sent',
            'sent_at' => now(),
            'channel_message_id' => $result['messages'][0]['id'] ?? null,
        ]);
    }

    protected function deliverTelegram(Message $message, Conversation $conversation): void
    {
        $integration = $this->integration($conversation->workspace_id, 'telegram');
        if (!$integration) {
            $this->fail($message, 'Telegram not connected');
            return;
        }

        $chatId = $conversation->channel_conversation_id;
        if (!$chatId) {
            $this->fail($message, 'No Telegram chat ID for this conversation');
            return;
        }

        $creds = $integration->credentials ?? [];
        $botToken = $creds['bot_token'] ?? '';
        $result = (new TelegramService($botToken))->sendMessage($chatId, $message->body_text ?? '');

        $message->update([
            'delivery_status' => 'sent',
            'sent_at' => now(),
            'channel_message_id' => isset($result['message_id']) ? "tg_{$chatId}_{$result['message_id']}" : null,
        ]);
    }

    protected function deliverSlack(Message $message, Conversation $conversation): void
    {
        $integration = $this->integration($conversation->workspace_id, 'slack');
        if (!$integration) {
            $this->fail($message, 'Slack not connected');
            return;
        }

        $channelId = $conversation->channel_conversation_id;
        if (!$channelId) {
            $this->fail($message, 'No Slack channel ID for this conversation');
            return;
        }

        $creds = $integration->credentials ?? [];
        $botToken = $creds['access_token'] ?? $creds['bot_token'] ?? '';

        app(SlackService::class)->postMessage($botToken, $channelId, $message->body_text ?? '');

        $message->update(['delivery_status' => 'sent', 'sent_at' => now()]);
    }

    /**
     * Safety net — GenerateAIReplyJob short-circuits chat widget delivery
     * before reaching this job, but if a future code path dispatches us
     * for a live_chat / chat conversation, the message row is already the
     * delivery (widget /history poll), so we just flip the status.
     */
    protected function deliverLiveChat(Message $message, Conversation $conversation): void
    {
        $sessionId = $conversation->channel_conversation_id;
        if ($sessionId) {
            app(LiveChatService::class)->sendToVisitor(
                $sessionId,
                $message->body_text ?? '',
                $message->from_name ?? 'AI Assistant',
                'ai',
            );
        }

        $message->update(['delivery_status' => 'sent', 'sent_at' => now()]);
    }

    protected function integration(int $workspaceId, string $channel): ?ChannelIntegration
    {
        return ChannelIntegration::where('workspace_id', $workspaceId)
            ->where('channel', $channel)
            ->where('status', 'active')
            ->first();
    }

    protected function fail(Message $message, string $reason): void
    {
        $message->update([
            'delivery_status' => 'failed',
            'delivery_error' => Str::limit($reason, 500),
        ]);
        Log::warning('SendAIReplyJob: ' . $reason, [
            'message_id' => $message->id,
        ]);
    }

    public function failed(?\Throwable $exception): void
    {
        Log::error('SendAIReplyJob permanently failed', [
            'message_id' => $this->message->id,
            'error' => $exception?->getMessage(),
        ]);
    }
}
