<?php

namespace App\Services\AiChat;

use App\Models\ChatLog;
use App\Models\ChatMessage;
use App\Models\ExternalChatMessage;
use App\Models\ExternalChatSession;
use App\Models\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use RuntimeException;

class MetaChatChannelService
{
    public function __construct(
        private readonly AiChatService $aiChatService,
        private readonly MetaChatSettingsService $settingsService
    ) {
    }

    public function handleVerification(string $channel, ?string $mode, ?string $verifyToken, ?string $challenge): ?string
    {
        if (!in_array($channel, ['whatsapp', 'messenger'], true)) {
            return null;
        }

        $expectedToken = $this->settingsService->getVerifyToken($channel);
        if ($mode !== 'subscribe' || blank($expectedToken) || !hash_equals((string) $expectedToken, (string) $verifyToken)) {
            return null;
        }

        return (string) $challenge;
    }

    public function handleWebhook(string $channel, array $payload): int
    {
        if (!in_array($channel, ['whatsapp', 'messenger'], true)) {
            return 0;
        }

        $messages = $channel === 'whatsapp'
            ? $this->extractWhatsAppMessages($payload)
            : $this->extractMessengerMessages($payload);

        $processed = 0;

        foreach ($messages as $message) {
            try {
                $this->processIncoming($channel, $message);
                $processed++;
            } catch (\Throwable $throwable) {
                Log::error('Meta chat message processing failed.', [
                    'channel' => $channel,
                    'message' => $throwable->getMessage(),
                    'external_user_id' => $message['external_user_id'] ?? null,
                ]);
            }
        }

        return $processed;
    }

    protected function processIncoming(string $channel, array $message): void
    {
        if (!$this->settingsService->isChannelEnabled($channel)) {
            return;
        }

        if (blank($message['message'] ?? null)) {
            return;
        }

        if (filled($message['provider_message_id'] ?? null) && ExternalChatMessage::query()
            ->where('channel', $channel)
            ->where('direction', 'inbound')
            ->where('provider_message_id', $message['provider_message_id'])
            ->exists()
        ) {
            return;
        }

        $customer = $this->matchLocalCustomer($message['phone'] ?? null);

        $session = ExternalChatSession::query()->firstOrNew([
            'channel' => $channel,
            'external_user_id' => (string) $message['external_user_id'],
        ]);

        $session->fill([
            'external_user_name' => $message['external_user_name'] ?? $session->external_user_name,
            'user_id' => $customer?->id,
            'phone' => $message['phone'] ?? $session->phone,
            'locale' => $message['locale'] ?? $session->locale,
            'last_message_at' => now(),
            'is_active' => true,
            'meta' => array_filter([
                'channel_payload' => Arr::get($message, 'meta.channel_payload'),
                'channel_meta' => Arr::get($message, 'meta.channel_meta'),
            ]),
        ]);
        $session->save();

        $inbound = ExternalChatMessage::query()->create([
            'external_chat_session_id' => $session->id,
            'channel' => $channel,
            'direction' => 'inbound',
            'role' => 'customer',
            'provider_message_id' => $message['provider_message_id'] ?? null,
            'message' => $message['message'],
            'language' => $message['language'] ?? null,
            'sent_at' => now(),
            'meta' => $message['meta'] ?? [],
        ]);

        ChatMessage::query()->create([
            'customer_id' => $customer?->id,
            'role' => 'customer',
            'message' => $message['message'],
            'language' => $message['language'] ?? null,
            'meta' => [
                'channel' => $channel,
                'external_chat_session_id' => $session->id,
                'external_chat_message_id' => $inbound->id,
                'external_user_id' => $session->external_user_id,
            ],
        ]);

        $aiSettings = $this->aiChatService->settings();
        if (!$aiSettings['chat_enabled'] || !$aiSettings['auto_reply_enabled']) {
            return;
        }

        $replyData = $this->aiChatService->previewReply((string) $message['message'], $customer);

        $outboundMeta = [
            'channel' => $channel,
            'external_user_id' => $session->external_user_id,
            'source_type' => $replyData['source_type'] ?? null,
            'source_id' => $replyData['source_id'] ?? null,
            'match_pct' => $replyData['match_pct'] ?? null,
            'product_answer_intent' => $replyData['product_answer_intent'] ?? null,
            'products' => $replyData['products'] ?? [],
        ];
        $providerReplyId = null;
        $delivered = false;
        $replyText = $this->aiChatService->cleanReplyText($replyData['reply'] ?? '');

        try {
            $providerReplyId = $this->sendChannelReply($channel, $session, $replyText);
            $delivered = true;
            $session->last_outbound_at = now();
            $session->save();
        } catch (\Throwable $throwable) {
            $outboundMeta['delivery_error'] = $throwable->getMessage();
            Log::error('Meta chat outbound reply failed.', [
                'channel' => $channel,
                'external_user_id' => $session->external_user_id,
                'message' => $throwable->getMessage(),
            ]);
        }

        $outbound = ExternalChatMessage::query()->create([
            'external_chat_session_id' => $session->id,
            'channel' => $channel,
            'direction' => 'outbound',
            'role' => 'assistant',
            'provider_message_id' => $providerReplyId,
            'message' => $replyText,
            'language' => $replyData['language'] ?? null,
            'sent_at' => $delivered ? now() : null,
            'meta' => $outboundMeta,
        ]);

        ChatMessage::query()->create([
            'customer_id' => $customer?->id,
            'role' => 'assistant',
            'message' => $replyText,
            'language' => $replyData['language'] ?? null,
            'meta' => [
                'channel' => $channel,
                'external_chat_session_id' => $session->id,
                'external_chat_message_id' => $outbound->id,
                'external_user_id' => $session->external_user_id,
                'delivered' => $delivered,
                'products' => $replyData['products'] ?? [],
            ],
        ]);

        ChatLog::query()->create([
            'customer_id' => $customer?->id,
            'normalized_question' => $replyData['normalized_question'] ?? Str::slug(Str::lower((string) $message['message'])),
            'question' => $message['message'],
            'reply' => $replyText,
            'language' => $replyData['language'] ?? $message['language'] ?? null,
            'query_type' => $replyData['query_type'] ?? null,
            'source_type' => $replyData['source_type'] ?? null,
            'source_id' => $replyData['source_id'] ?? null,
            'matched_keyword' => $replyData['matched_keyword'] ?? null,
            'confidence_score' => (int) ($replyData['confidence_score'] ?? 0),
            'fallback_used' => (bool) ($replyData['fallback_used'] ?? false),
            'is_corrected' => ($replyData['source_type'] ?? null) === 'correction',
            'meta' => [
                'channel' => $channel,
                'external_chat_session_id' => $session->id,
                'inbound_external_message_id' => $inbound->id,
                'outbound_external_message_id' => $outbound->id,
                'external_user_id' => $session->external_user_id,
                'delivered' => $delivered,
                'products' => $replyData['products'] ?? [],
                'product_answer_intent' => $replyData['product_answer_intent'] ?? null,
            ],
        ]);
    }

    protected function sendChannelReply(string $channel, ExternalChatSession $session, string $reply): ?string
    {
        return match ($channel) {
            'whatsapp' => $this->sendWhatsAppReply($session, $reply),
            'messenger' => $this->sendMessengerReply($session, $reply),
            default => null,
        };
    }

    protected function sendWhatsAppReply(ExternalChatSession $session, string $reply): ?string
    {
        $settings = $this->settingsService->getSettings();
        $token = Arr::get($settings, 'whatsapp.access_token');
        $phoneNumberId = Arr::get($settings, 'whatsapp.phone_number_id');
        $graphVersion = Arr::get($settings, 'graph_version', 'v23.0');

        if (blank($token) || blank($phoneNumberId)) {
            throw new RuntimeException('WhatsApp access token or phone number ID is missing.');
        }

        $response = Http::timeout(15)
            ->withToken($token)
            ->post("https://graph.facebook.com/{$graphVersion}/{$phoneNumberId}/messages", [
                'messaging_product' => 'whatsapp',
                'recipient_type' => 'individual',
                'to' => $session->external_user_id,
                'type' => 'text',
                'text' => [
                    'body' => $reply,
                ],
            ]);

        if (!$response->successful()) {
            throw new RuntimeException('WhatsApp API error: ' . $response->body());
        }

        return Arr::get($response->json(), 'messages.0.id');
    }

    protected function sendMessengerReply(ExternalChatSession $session, string $reply): ?string
    {
        $settings = $this->settingsService->getSettings();
        $token = Arr::get($settings, 'messenger.page_access_token');
        $graphVersion = Arr::get($settings, 'graph_version', 'v23.0');

        if (blank($token)) {
            throw new RuntimeException('Messenger page access token is missing.');
        }

        $response = Http::timeout(15)
            ->withToken($token)
            ->post("https://graph.facebook.com/{$graphVersion}/me/messages", [
                'messaging_type' => 'RESPONSE',
                'recipient' => [
                    'id' => $session->external_user_id,
                ],
                'message' => [
                    'text' => $reply,
                ],
            ]);

        if (!$response->successful()) {
            throw new RuntimeException('Messenger API error: ' . $response->body());
        }

        return Arr::get($response->json(), 'message_id');
    }

    protected function extractWhatsAppMessages(array $payload): array
    {
        $messages = [];

        foreach (Arr::get($payload, 'entry', []) as $entry) {
            foreach (Arr::get($entry, 'changes', []) as $change) {
                if (Arr::get($change, 'field') !== 'messages') {
                    continue;
                }

                $value = Arr::get($change, 'value', []);
                $contactName = Arr::get($value, 'contacts.0.profile.name');

                foreach (Arr::get($value, 'messages', []) as $message) {
                    $text = $this->extractWhatsAppText($message);

                    if (blank($text)) {
                        continue;
                    }

                    $messages[] = [
                        'external_user_id' => (string) Arr::get($message, 'from'),
                        'external_user_name' => $contactName,
                        'phone' => (string) Arr::get($message, 'from'),
                        'provider_message_id' => Arr::get($message, 'id'),
                        'message' => $text,
                        'language' => Arr::get($value, 'contacts.0.profile.locale'),
                        'locale' => Arr::get($value, 'contacts.0.profile.locale'),
                        'meta' => [
                            'channel_meta' => [
                                'phone_number_id' => Arr::get($value, 'metadata.phone_number_id'),
                                'display_phone_number' => Arr::get($value, 'metadata.display_phone_number'),
                                'type' => Arr::get($message, 'type'),
                            ],
                            'channel_payload' => $message,
                        ],
                    ];
                }
            }
        }

        return $messages;
    }

    protected function extractMessengerMessages(array $payload): array
    {
        $messages = [];
        $expectedPageId = Arr::get($this->settingsService->getChannelConfig('messenger'), 'page_id');

        foreach (Arr::get($payload, 'entry', []) as $entry) {
            $entryPageId = (string) Arr::get($entry, 'id');
            if (filled($expectedPageId) && $entryPageId !== (string) $expectedPageId) {
                continue;
            }

            foreach (Arr::get($entry, 'messaging', []) as $event) {
                if (Arr::get($event, 'message.is_echo')) {
                    continue;
                }

                $text = trim((string) Arr::get($event, 'message.text', ''));
                if ($text === '') {
                    continue;
                }

                $messages[] = [
                    'external_user_id' => (string) Arr::get($event, 'sender.id'),
                    'external_user_name' => null,
                    'phone' => null,
                    'provider_message_id' => Arr::get($event, 'message.mid'),
                    'message' => $text,
                    'language' => null,
                    'locale' => null,
                    'meta' => [
                        'channel_meta' => [
                            'page_id' => $entryPageId,
                            'recipient_id' => Arr::get($event, 'recipient.id'),
                        ],
                        'channel_payload' => $event,
                    ],
                ];
            }
        }

        return $messages;
    }

    protected function extractWhatsAppText(array $message): ?string
    {
        return match (Arr::get($message, 'type')) {
            'text' => trim((string) Arr::get($message, 'text.body', '')),
            'button' => trim((string) Arr::get($message, 'button.text', '')),
            'interactive' => trim((string) (
                Arr::get($message, 'interactive.button_reply.title')
                ?: Arr::get($message, 'interactive.list_reply.title')
                ?: Arr::get($message, 'interactive.nfm_reply.body')
            )),
            default => null,
        };
    }

    protected function matchLocalCustomer(?string $phone): ?User
    {
        if (blank($phone)) {
            return null;
        }

        $normalized = preg_replace('/\D+/', '', (string) $phone);
        if ($normalized === '') {
            return null;
        }

        $lastDigits = strlen($normalized) > 10 ? substr($normalized, -10) : $normalized;

        return User::query()
            ->where('phone', (string) $phone)
            ->orWhere('phone', $normalized)
            ->orWhere('phone', 'like', '%' . $lastDigits)
            ->first();
    }
}
