<?php

namespace App\Services\Bot;

use App\Models\CallbackRequest;
use App\Models\Conversation;
use App\Models\Message;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class ProcessIncomingCustomerMessage
{
    public function __construct(
        private readonly MatchIncomingMessage $matcher,
        private readonly AiRagService $ragService,
    ) {}

    /**
     * @param  array{provider?: string, provider_message_id?: ?string, provider_event_id?: ?string, provider_timestamp?: mixed}  $providerMetadata
     */
    public function handle(Conversation $conversation, string $body, string $locale = 'bn', ?string $phone = null, ?int $replyToMessageId = null, array $providerMetadata = []): IncomingMessageResult
    {
        return DB::transaction(function () use ($conversation, $body, $locale, $phone, $replyToMessageId, $providerMetadata): IncomingMessageResult {
            $conversation = $conversation->fresh(['contact', 'organization']);
            $sentAt = Carbon::now();

            if ($phone !== null && $conversation->contact->phone === null) {
                $conversation->contact->forceFill(['phone' => $phone])->save();
            }

            // Command: /name [New Name] or /নাম [নতুন নাম]
            $trimmedBody = trim($body);
            $bodyLower = mb_strtolower($trimmedBody);
            
            // Command: /reset (clears human takeover for testing/restarting)
            if ($bodyLower === '/reset') {
                $conversation->forceFill([
                    'status' => 'open',
                    'human_takeover_at' => null,
                ])->save();

                $botMessage = $conversation->messages()->create([
                    'sender_type' => 'bot',
                    'body' => 'Conversation reset. Bot is active again.',
                    'locale' => $locale,
                    'sent_at' => Carbon::now(),
                ]);

                return new IncomingMessageResult('answered', $conversation->messages()->latest()->first(), $botMessage);
            }
            
            $newName = null;
            if (str_starts_with($bodyLower, '/name ')) {
                $newName = trim(Str::substr($trimmedBody, 6));
            } elseif (str_starts_with($bodyLower, '/নাম ')) {
                $newName = trim(Str::substr($trimmedBody, mb_strlen('/নাম ')));
            }

            if ($newName !== null && ! empty($newName)) {
                $conversation->contact->update(['name' => $newName]);

                $customerMessage = $conversation->messages()->create([
                    'sender_type' => 'customer',
                    'body' => $body,
                    'locale' => $locale,
                    'sent_at' => $sentAt,
                    'provider' => $providerMetadata['provider'] ?? null,
                    'provider_message_id' => $providerMetadata['provider_message_id'] ?? null,
                    'provider_event_id' => $providerMetadata['provider_event_id'] ?? null,
                    'provider_timestamp' => $providerMetadata['provider_timestamp'] ?? null,
                ]);

                $botMessageBody = trans('bot.name_updated', ['name' => $newName], $locale);

                $botMessage = $conversation->messages()->create([
                    'sender_type' => 'bot',
                    'body' => $botMessageBody,
                    'locale' => $locale,
                    'sent_at' => Carbon::now(),
                ]);

                $conversation->forceFill([
                    'last_message_at' => $botMessage->sent_at,
                ])->save();

                return new IncomingMessageResult('answered', $customerMessage, $botMessage);
            }

            $metadata = [];
            if ($replyToMessageId !== null) {
                $replyMsg = $conversation->messages()->find($replyToMessageId);
                if ($replyMsg) {
                    $metadata['reply_to'] = [
                        'id' => $replyMsg->id,
                        'body' => Str::limit($replyMsg->body, 60),
                    ];
                }
            }

            $customerMessage = $conversation->messages()->create([
                'sender_type' => 'customer',
                'body' => $body,
                'locale' => $locale,
                'sent_at' => $sentAt,
                'metadata' => empty($metadata) ? null : $metadata,
                'provider' => $providerMetadata['provider'] ?? null,
                'provider_message_id' => $providerMetadata['provider_message_id'] ?? null,
                'provider_event_id' => $providerMetadata['provider_event_id'] ?? null,
                'provider_timestamp' => $providerMetadata['provider_timestamp'] ?? null,
            ]);

            $conversation->forceFill([
                'last_message_at' => $sentAt,
            ])->save();

            if ($conversation->human_takeover_at !== null || $conversation->status === 'pending_human') {
                return new IncomingMessageResult('human_takeover_active', $customerMessage);
            }

            $topCandidates = $this->matcher->topCandidates($conversation->organization, $body, $locale, 3);
            $candidate = $topCandidates[0] ?? null;

            if ($candidate !== null) {
                $contexts = array_map(fn($c) => $c->responseTemplate->body, $topCandidates);
                
                $history = $conversation->messages()
                    ->where('id', '<', $customerMessage->id)
                    ->latest('sent_at')
                    ->limit(6)
                    ->get()
                    ->reverse()
                    ->map(fn($msg) => [
                        'role' => $msg->sender_type === 'bot' ? 'bot' : 'customer',
                        'content' => $msg->body,
                    ])
                    ->values()
                    ->toArray();

                $ragResponse = $this->ragService->generateResponse($conversation->organization, $body, $contexts, $history);

                if ($ragResponse !== null) {
                    $botMessage = $this->createBotMessage($conversation, $ragResponse, $locale, [
                        'matched_intent_id' => $candidate->intent->id,
                        'match_type' => 'rag_response',
                    ], $candidate->confidence);

                    return new IncomingMessageResult('answered', $customerMessage, $botMessage, $candidate);
                }
            }

            // Fallback to static matching if RAG fails or returns null
            if ($candidate !== null && $candidate->confidence >= (float) $candidate->intent->confidence_threshold) {
                $botMessage = $this->createBotMessage($conversation, $candidate->responseTemplate->body, $locale, [
                    'matched_intent_id' => $candidate->intent->id,
                    'response_template_id' => $candidate->responseTemplate->id,
                    'matched_phrase' => $candidate->matchedPhrase,
                    'match_type' => 'approved_response',
                ], $candidate->confidence);

                return new IncomingMessageResult('answered', $customerMessage, $botMessage, $candidate);
            }

            if ($candidate !== null && $candidate->confidence >= 0.45) {
                $botMessage = $this->createBotMessage($conversation, "Did you mean {$candidate->intent->name}?", $locale, [
                    'clarification_intent_id' => $candidate->intent->id,
                    'matched_phrase' => $candidate->matchedPhrase,
                    'match_type' => 'clarification',
                ], $candidate->confidence);

                return new IncomingMessageResult('clarification_requested', $customerMessage, $botMessage, $candidate);
            }

            $unknownQuestion = $conversation->unknownQuestions()->create([
                'organization_id' => $conversation->organization_id,
                'contact_id' => $conversation->contact_id,
                'message_id' => $customerMessage->id,
                'body' => $body,
                'locale' => $locale,
                'status' => 'open',
                'confidence' => $candidate?->confidence,
            ]);

            $callbackRequest = $this->openCallbackRequest($conversation);
            $botBody = $conversation->contact->phone === null
                ? trans('bot.share_phone', [], $locale)
                : trans('bot.agent_follow_up', [], $locale);

            $botMessage = $this->createBotMessage($conversation, $botBody, $locale, [
                'unknown_question_id' => $unknownQuestion->id,
                'callback_request_id' => $callbackRequest->id,
                'match_type' => 'handoff',
            ]);

            $conversation->forceFill([
                'status' => 'pending_human',
                'human_takeover_at' => Carbon::now(),
            ])->save();

            return new IncomingMessageResult('handoff_requested', $customerMessage, $botMessage, $candidate, $unknownQuestion, $callbackRequest);
        });
    }

    /**
     * @param  array<string, mixed>  $metadata
     */
    private function createBotMessage(Conversation $conversation, string $body, string $locale, array $metadata, ?float $confidence = null): Message
    {
        $body = str_replace(
            ['{{contact.name}}', '{{contact.phone}}'],
            [$conversation->contact->name ?? 'there', $conversation->contact->phone ?? ''],
            $body
        );

        // Dynamically adjust time-based greetings based on Bangladesh time
        $hour = Carbon::now()->timezone('Asia/Dhaka')->hour;
        $bnGreeting = 'শুভ সন্ধ্যা';
        $enGreeting = 'Good evening';
        
        if ($hour >= 5 && $hour < 12) {
            $bnGreeting = 'শুভ সকাল';
            $enGreeting = 'Good morning';
        } elseif ($hour >= 12 && $hour < 18) {
            $bnGreeting = 'শুভ অপরাহ্ন';
            $enGreeting = 'Good afternoon';
        }
        
        $body = preg_replace('/(?i)(good\s+)?(morning|afternoon|evening)/', $enGreeting, $body);
        $body = preg_replace('/শুভ\s*(সকাল|অপরাহ্ন|সন্ধ্যা)/u', $bnGreeting, $body);

        $message = $conversation->messages()->create([
            'sender_type' => 'bot',
            'body' => $body,
            'locale' => $locale,
            'confidence' => $confidence,
            'metadata' => $metadata,
            'sent_at' => Carbon::now(),
        ]);

        $conversation->forceFill([
            'last_message_at' => $message->sent_at,
        ])->save();

        return $message;
    }

    private function openCallbackRequest(Conversation $conversation): CallbackRequest
    {
        return $conversation->callbackRequests()
            ->whereIn('status', ['needs_phone', 'requested'])
            ->firstOrCreate([
                'organization_id' => $conversation->organization_id,
                'contact_id' => $conversation->contact_id,
                'status' => $conversation->contact->phone === null ? 'needs_phone' : 'requested',
            ], [
                'phone' => $conversation->contact->phone,
                'requested_at' => Carbon::now(),
            ]);
    }
}
