<?php

namespace App\Http\Controllers;

use App\Models\Contact;
use App\Models\Deal;
use App\Models\DealStage;
use App\Models\MessengerChannel;
use App\Models\MessengerConversation;
use App\Models\MessengerMessage;
use App\Models\MessengerSetting;
use App\Models\Pipeline;
use App\Models\Task;
use App\Models\User;
use App\Support\CrmModuleManager;
use App\Support\MessengerAccess;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class MessengerWebhookController extends Controller
{
    public function handle(Request $request, CrmModuleManager $moduleManager): JsonResponse
    {
        $settings = MessengerSetting::query()->first();
        if (! $settings || ! $settings->is_active) {
            return response()->json(['status' => 'ignored'], 202);
        }

        $secret = (string) ($settings->webhook_secret ?? '');
        if ($secret !== '') {
            $incomingSecret = (string) ($request->header('X-CRM-Webhook-Secret', $request->input('secret', '')));
            if ($incomingSecret === '' || ! hash_equals($secret, $incomingSecret)) {
                return response()->json(['message' => 'Unauthorized'], 401);
            }
        }

        $payload = is_array($request->all()) ? $request->all() : [];

        return DB::transaction(function () use ($payload, $moduleManager, $settings): JsonResponse {
            $provider = MessengerAccess::normalizeProvider($payload['provider'] ?? $settings->provider ?? 'telegram');

            $channelExternalId = $this->nullableText($payload['channel_external_id'] ?? $payload['channel_id'] ?? null);
            $channelHandle = $this->nullableText($payload['channel_handle'] ?? $payload['channel'] ?? null);
            $channelName = $this->nullableText($payload['channel_name'] ?? $payload['channel_title'] ?? null) ?: (MessengerAccess::providerOptions()[$provider] ?? ucfirst($provider));

            $channelQuery = MessengerChannel::query()->where('provider', $provider);
            if ($channelExternalId) {
                $channelQuery->where('external_id', $channelExternalId);
            } elseif ($channelHandle) {
                $channelQuery->where('handle', $channelHandle);
            }
            $channel = $channelQuery->first();

            if (! $channel) {
                $channel = MessengerChannel::query()->create([
                    'provider' => $provider,
                    'external_id' => $channelExternalId,
                    'name' => $channelName,
                    'handle' => $channelHandle,
                    'status' => 'active',
                    'is_default' => false,
                ]);
            }

            $conversationExternalId = $this->nullableText($payload['conversation_external_id'] ?? $payload['thread_id'] ?? null);
            $contactName = $this->nullableText($payload['contact_name'] ?? $payload['sender_name'] ?? null);
            $contactHandle = $this->nullableText($payload['contact_handle'] ?? $payload['sender'] ?? null);
            $status = $this->normalizeStatus($payload['conversation_status'] ?? null);

            $conversationQuery = MessengerConversation::query()
                ->where('provider', $provider)
                ->where('channel_id', $channel->id);

            if ($conversationExternalId) {
                $conversationQuery->where('external_id', $conversationExternalId);
            } elseif ($contactHandle) {
                $conversationQuery->where('contact_handle', $contactHandle);
            }

            $conversation = $conversationQuery->first();
            $isNewConversation = false;
            if (! $conversation) {
                $isNewConversation = true;
                $conversation = MessengerConversation::query()->create([
                    'channel_id' => $channel->id,
                    'provider' => $provider,
                    'external_id' => $conversationExternalId,
                    'contact_name' => $contactName,
                    'contact_handle' => $contactHandle,
                    'status' => $status ?? 'open',
                    'user_id' => $this->resolveConversationOwnerId($payload, $settings),
                ]);
            } elseif ($conversation->user_id === null) {
                $ownerId = $this->resolveConversationOwnerId($payload, $settings);
                if ($ownerId !== null) {
                    $conversation->update(['user_id' => $ownerId]);
                }
            }

            [$contact, $contactCreated] = $this->resolveOrCreateContact(
                $conversation,
                $contactName,
                $contactHandle,
                $provider,
                $settings
            );

            if ($contact) {
                $conversation->update([
                    'contact_id' => $contact->id,
                    'contact_name' => trim((string) $contact->full_name) !== '' ? trim((string) $contact->full_name) : $contact->first_name,
                    'contact_handle' => $conversation->contact_handle ?: ($contact->email ?: $contact->phone),
                ]);

                if ($contact->last_contacted_at === null || $contact->last_contacted_at->lt(now()->subMinute())) {
                    $contact->last_contacted_at = now();
                    $contact->save();
                }
            }

            if ($isNewConversation && $contact && $contactCreated) {
                $this->runNewContactAutomation($conversation, $contact, $settings);
            }

            $direction = $this->normalizeDirection($payload['direction'] ?? null);
            $messageStatus = $this->normalizeMessageStatus($payload['status'] ?? null);
            $body = $this->nullableText($payload['body'] ?? $payload['text'] ?? null);
            $sentAt = $payload['sent_at'] ?? $payload['timestamp'] ?? null;
            $attachments = $payload['attachments'] ?? null;
            if (! is_array($attachments)) {
                $attachments = null;
            }

            $messageExternalId = $this->nullableText($payload['message_external_id'] ?? $payload['message_id'] ?? $payload['external_id'] ?? null);

            $messageData = [
                'conversation_id' => $conversation->id,
                'external_id' => $messageExternalId,
                'direction' => $direction,
                'body' => $body,
                'attachments' => $attachments,
                'status' => $messageStatus,
                'sent_at' => $sentAt,
                'user_id' => $payload['user_id'] ?? null,
                'meta' => $this->filterMeta($payload),
            ];

            $existingMessage = null;
            if ($messageExternalId) {
                $existingMessage = MessengerMessage::query()
                    ->where('conversation_id', $conversation->id)
                    ->where('external_id', $messageExternalId)
                    ->first();
            }

            if ($existingMessage) {
                $messageData = $moduleManager->applyPayloadHooks('messengers.messages.update', $messageData, [
                    'hook' => 'messengers.messages.update',
                    'conversation_id' => $conversation->id,
                    'message_id' => $existingMessage->id,
                    'source' => 'webhook',
                ], array_keys($messageData));

                $existingMessage->fill($messageData)->save();
                $messageId = $existingMessage->id;
            } else {
                $messageData = $moduleManager->applyPayloadHooks('messengers.messages.store', $messageData, [
                    'hook' => 'messengers.messages.store',
                    'conversation_id' => $conversation->id,
                    'source' => 'webhook',
                ], array_keys($messageData));

                $message = MessengerMessage::query()->create($messageData);
                $messageId = $message->id;
            }

            $conversation->update([
                'last_message_at' => $sentAt ?? now(),
            ]);

            return response()->json([
                'status' => 'ok',
                'conversation_id' => $conversation->id,
                'message_id' => $messageId,
                'contact_id' => $conversation->contact_id,
                'assigned_user_id' => $conversation->user_id,
            ]);
        });
    }

    private function resolveConversationOwnerId(array $payload, MessengerSetting $settings): ?int
    {
        $candidateId = isset($payload['user_id']) ? (int) $payload['user_id'] : 0;
        if ($candidateId <= 0 && isset($payload['user_email'])) {
            $candidateId = (int) User::query()
                ->where('email', trim((string) $payload['user_email']))
                ->value('id');
        }

        if ($candidateId > 0) {
            $candidate = User::query()->find($candidateId);
            if ($candidate && MessengerAccess::canAcceptIncoming($candidate, $settings)) {
                return $candidate->id;
            }
        }

        return MessengerAccess::firstAllowedOperatorId($settings);
    }

    /**
     * @return array{0: Contact|null, 1: bool}
     */
    private function resolveOrCreateContact(
        MessengerConversation $conversation,
        ?string $contactName,
        ?string $contactHandle,
        string $provider,
        MessengerSetting $settings
    ): array {
        if ($conversation->contact_id) {
            $contact = Contact::query()->find($conversation->contact_id);
            if ($contact) {
                $this->hydrateContactFromHandle($contact, $contactName, $contactHandle);

                return [$contact, false];
            }
        }

        $contact = null;
        if ($contactHandle) {
            if (filter_var($contactHandle, FILTER_VALIDATE_EMAIL)) {
                $contact = Contact::query()->whereRaw('lower(email) = ?', [strtolower($contactHandle)])->first();
            } else {
                $contact = Contact::query()->where('phone', $contactHandle)->first();
            }
        }

        if (! $contact && $contactName) {
            $parts = preg_split('/\s+/u', trim($contactName), -1, PREG_SPLIT_NO_EMPTY) ?: [];
            if (count($parts) >= 2) {
                $contact = Contact::query()
                    ->whereRaw('lower(first_name) = ?', [mb_strtolower($parts[0])])
                    ->whereRaw('lower(last_name) = ?', [mb_strtolower(implode(' ', array_slice($parts, 1)))])
                    ->first();
            }
        }

        if ($contact) {
            $this->hydrateContactFromHandle($contact, $contactName, $contactHandle);

            return [$contact, false];
        }

        if (! ($settings->auto_create_contact ?? true)) {
            return [null, false];
        }

        $firstName = 'Messenger';
        $lastName = null;
        if ($contactName) {
            $nameParts = preg_split('/\s+/u', trim($contactName), -1, PREG_SPLIT_NO_EMPTY) ?: [];
            if ($nameParts !== []) {
                $firstName = (string) array_shift($nameParts);
                $lastName = $nameParts === [] ? null : implode(' ', $nameParts);
            }
        } elseif ($contactHandle) {
            $firstName = $contactHandle;
        }

        $ownerId = $conversation->user_id ?: ($settings->new_contact_action_user_id ?: MessengerAccess::firstAllowedOperatorId($settings));
        $contact = Contact::query()->create([
            'first_name' => mb_substr(trim($firstName), 0, 255),
            'last_name' => $lastName ? mb_substr(trim($lastName), 0, 255) : null,
            'title' => null,
            'email' => $contactHandle && filter_var($contactHandle, FILTER_VALIDATE_EMAIL) ? mb_substr($contactHandle, 0, 255) : null,
            'phone' => $contactHandle && ! filter_var($contactHandle, FILTER_VALIDATE_EMAIL) ? mb_substr($contactHandle, 0, 50) : null,
            'company_id' => null,
            'owner_id' => $ownerId ?: null,
            'source' => 'messenger:'.$provider,
            'last_contacted_at' => now(),
            'notes' => 'Auto created from messenger conversation #'.$conversation->id,
        ]);

        return [$contact, true];
    }

    private function runNewContactAutomation(MessengerConversation $conversation, Contact $contact, MessengerSetting $settings): void
    {
        $action = trim((string) ($settings->new_contact_action ?? 'none'));
        if (! in_array($action, ['none', 'task', 'deal'], true) || $action === 'none') {
            return;
        }

        $targetUserId = $conversation->user_id ?: ($settings->new_contact_action_user_id ?: null);
        $contactTitle = trim((string) $contact->full_name) !== '' ? trim((string) $contact->full_name) : $contact->first_name;

        $meta = is_array($conversation->meta) ? $conversation->meta : [];
        if ($action === 'task') {
            $task = Task::query()->create([
                'title' => 'Messenger lead: '.$contactTitle,
                'description' => 'Auto-created task from incoming messenger conversation.',
                'deal_id' => null,
                'company_id' => $contact->company_id,
                'contact_id' => $contact->id,
                'creator_id' => $targetUserId,
                'assignee_id' => $targetUserId,
                'status' => 'todo',
                'priority' => 'medium',
                'due_at' => now()->addDay(),
            ]);

            $meta['new_contact_action'] = [
                'type' => 'task',
                'result_id' => $task->id,
                'created_at' => now()->toISOString(),
            ];
        }

        if ($action === 'deal') {
            $pipeline = Pipeline::query()
                ->where('is_default', true)
                ->orderBy('id')
                ->first() ?: Pipeline::query()->orderBy('id')->first();

            $stage = $pipeline
                ? DealStage::query()->where('pipeline_id', $pipeline->id)->orderBy('sort_order')->orderBy('id')->first()
                : null;

            if ($pipeline && $stage) {
                $deal = Deal::query()->create([
                    'title' => 'Messenger lead: '.$contactTitle,
                    'pipeline_id' => $pipeline->id,
                    'stage_id' => $stage->id,
                    'company_id' => $contact->company_id,
                    'contact_id' => $contact->id,
                    'owner_id' => $targetUserId,
                    'amount' => 0,
                    'currency' => 'USD',
                    'priority' => 'medium',
                    'status' => 'open',
                    'expected_close_at' => now()->addDays(14)->toDateString(),
                    'closed_at' => null,
                    'last_activity_at' => now(),
                    'source' => 'messenger:'.$conversation->provider,
                    'lost_reason' => null,
                    'description' => 'Auto-created deal from incoming messenger conversation.',
                ]);

                $meta['new_contact_action'] = [
                    'type' => 'deal',
                    'result_id' => $deal->id,
                    'created_at' => now()->toISOString(),
                ];
            }
        }

        $conversation->update([
            'meta' => $meta,
        ]);
    }

    private function hydrateContactFromHandle(Contact $contact, ?string $contactName, ?string $contactHandle): void
    {
        $hasChanges = false;

        if (($contact->first_name ?? '') === '' && $contactName) {
            $nameParts = preg_split('/\s+/u', trim($contactName), -1, PREG_SPLIT_NO_EMPTY) ?: [];
            if ($nameParts !== []) {
                $contact->first_name = (string) array_shift($nameParts);
                $contact->last_name = $contact->last_name ?: ($nameParts === [] ? null : implode(' ', $nameParts));
                $hasChanges = true;
            }
        }

        if ($contactHandle) {
            if (filter_var($contactHandle, FILTER_VALIDATE_EMAIL) && ($contact->email ?? '') === '') {
                $contact->email = mb_substr($contactHandle, 0, 255);
                $hasChanges = true;
            }

            if (! filter_var($contactHandle, FILTER_VALIDATE_EMAIL) && ($contact->phone ?? '') === '') {
                $contact->phone = mb_substr($contactHandle, 0, 50);
                $hasChanges = true;
            }
        }

        $contact->last_contacted_at = now();
        $hasChanges = true;

        if ($hasChanges) {
            $contact->save();
        }
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array<string, mixed>|null
     */
    private function filterMeta(array $payload): ?array
    {
        $meta = Arr::except($payload, ['secret', 'token', 'api_key', 'api_secret']);

        return $meta === [] ? null : $meta;
    }

    private function nullableText(mixed $value): ?string
    {
        $value = trim((string) $value);

        return $value === '' ? null : $value;
    }

    private function normalizeDirection(mixed $value): string
    {
        $value = strtolower(trim((string) $value));
        if (in_array($value, ['incoming', 'in'], true)) {
            return 'inbound';
        }
        if (in_array($value, ['outgoing', 'out'], true)) {
            return 'outbound';
        }

        return in_array($value, ['inbound', 'outbound'], true) ? $value : 'inbound';
    }

    private function normalizeMessageStatus(mixed $value): string
    {
        $value = strtolower(trim((string) $value));

        return in_array($value, ['new', 'sent', 'delivered', 'read', 'failed'], true) ? $value : 'new';
    }

    private function normalizeStatus(mixed $value): ?string
    {
        $value = strtolower(trim((string) $value));

        return in_array($value, ['open', 'pending', 'closed'], true) ? $value : null;
    }
}

