<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\MessengerConversationResource;
use App\Models\Contact;
use App\Models\MessengerChannel;
use App\Models\MessengerConversation;
use App\Models\MessengerSetting;
use App\Support\AccessControl;
use App\Support\CrmModuleManager;
use App\Support\MessengerAccess;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class MessengerConversationController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(MessengerConversation::class, 'conversation');
    }

    public function index(Request $request)
    {
        $user = $request->user();
        $settings = MessengerSetting::query()->first();
        $canAcceptIncoming = MessengerAccess::canAcceptIncoming($user, $settings);

        $search = trim((string) $request->input('q', ''));
        $status = trim((string) $request->input('status', ''));
        $provider = trim((string) $request->input('provider', ''));
        if ($provider !== '') {
            $provider = MessengerAccess::normalizeProvider($provider);
        }
        $channelId = $request->integer('channel_id');
        $userId = $request->integer('user_id');
        $dateFrom = trim((string) $request->input('date_from', ''));
        $dateTo = trim((string) $request->input('date_to', ''));

        $query = MessengerConversation::query()
            ->with(['channel', 'user', 'contact', 'lastMessage'])
            ->withCount('messages')
            ->when($search !== '', function ($builder) use ($search): void {
                $builder->where(function ($sub) use ($search): void {
                    $sub->where('contact_name', 'like', "%{$search}%")
                        ->orWhere('contact_handle', 'like', "%{$search}%")
                        ->orWhere('external_id', 'like', "%{$search}%");
                });
            })
            ->when($status !== '', fn ($builder) => $builder->where('status', $status))
            ->when($provider !== '', fn ($builder) => $builder->where('provider', $provider))
            ->when($channelId > 0, fn ($builder) => $builder->where('channel_id', $channelId))
            ->when($dateFrom !== '', fn ($builder) => $builder->whereDate('last_message_at', '>=', $dateFrom))
            ->when($dateTo !== '', fn ($builder) => $builder->whereDate('last_message_at', '<=', $dateTo));

        if (! AccessControl::isElevated($user)) {
            if ($canAcceptIncoming) {
                $query->where(function ($builder) use ($user): void {
                    $builder->where('user_id', $user->id)
                        ->orWhereNull('user_id');
                });
            } else {
                $query->where('user_id', $user->id);
            }
        } elseif ($userId > 0) {
            $query->where('user_id', $userId);
        }

        $conversations = $query
            ->orderByDesc('last_message_at')
            ->orderByDesc('id')
            ->paginate(30)
            ->withQueryString();

        return MessengerConversationResource::collection($conversations);
    }

    public function store(Request $request, CrmModuleManager $moduleManager): MessengerConversationResource
    {
        $settings = MessengerSetting::query()->first();
        $validated = $request->validate([
            'channel_id' => ['required', 'exists:messenger_channels,id'],
            'external_id' => ['nullable', 'string', 'max:255'],
            'contact_name' => ['nullable', 'string', 'max:255'],
            'contact_handle' => ['nullable', 'string', 'max:120'],
            'status' => ['required', Rule::in(['open', 'pending', 'closed'])],
            'user_id' => ['nullable', 'exists:users,id'],
            'contact_id' => ['nullable', 'exists:contacts,id'],
            'meta' => ['nullable', 'array'],
        ]);

        $channel = MessengerChannel::query()->findOrFail($validated['channel_id']);
        $provider = $channel->provider;

        $payload = [
            'channel_id' => $channel->id,
            'provider' => $provider,
            'external_id' => $this->nullableText($validated['external_id'] ?? null),
            'contact_name' => $this->nullableText($validated['contact_name'] ?? null),
            'contact_handle' => $this->nullableText($validated['contact_handle'] ?? null),
            'status' => (string) $validated['status'],
            'user_id' => $validated['user_id'] ?? null,
            'contact_id' => $validated['contact_id'] ?? null,
            'meta' => $validated['meta'] ?? null,
        ];

        if (! AccessControl::isElevated($request->user())) {
            $payload['user_id'] = MessengerAccess::canAcceptIncoming($request->user(), $settings)
                ? $request->user()->id
                : null;
        }

        if ($payload['external_id']) {
            $exists = MessengerConversation::query()
                ->where('provider', $provider)
                ->where('external_id', $payload['external_id'])
                ->exists();

            if ($exists) {
                abort(422, 'Conversation external ID must be unique for this provider.');
            }
        }

        if ($payload['contact_id']) {
            $contact = Contact::query()->find($payload['contact_id']);
            if ($contact) {
                $payload['contact_name'] = trim((string) $contact->full_name) !== '' ? trim((string) $contact->full_name) : $contact->first_name;
                $payload['contact_handle'] = $payload['contact_handle'] ?: ($contact->email ?: $contact->phone);
            }
        }

        $payload = $moduleManager->applyPayloadHooks('messengers.conversations.store', $payload, [
            'hook' => 'messengers.conversations.store',
            'user_id' => $request->user()->id,
            'source' => 'api',
        ], array_keys($payload));

        $conversation = MessengerConversation::query()->create($payload);

        return MessengerConversationResource::make($conversation->load(['channel', 'user', 'contact', 'lastMessage'])->loadCount('messages'));
    }

    public function show(MessengerConversation $conversation): MessengerConversationResource
    {
        return MessengerConversationResource::make($conversation->load(['channel', 'user', 'contact', 'lastMessage'])->loadCount('messages'));
    }

    public function update(Request $request, MessengerConversation $conversation, CrmModuleManager $moduleManager): MessengerConversationResource
    {
        $settings = MessengerSetting::query()->first();
        $validated = $request->validate([
            'channel_id' => ['nullable', 'exists:messenger_channels,id'],
            'external_id' => ['nullable', 'string', 'max:255'],
            'contact_name' => ['nullable', 'string', 'max:255'],
            'contact_handle' => ['nullable', 'string', 'max:120'],
            'status' => ['required', Rule::in(['open', 'pending', 'closed'])],
            'user_id' => ['nullable', 'exists:users,id'],
            'contact_id' => ['nullable', 'exists:contacts,id'],
            'meta' => ['nullable', 'array'],
        ]);

        $channelId = $validated['channel_id'] ?? $conversation->channel_id;
        $channel = MessengerChannel::query()->findOrFail($channelId);
        $provider = $channel->provider;

        $payload = [
            'channel_id' => $channel->id,
            'provider' => $provider,
            'external_id' => $this->nullableText($validated['external_id'] ?? $conversation->external_id),
            'contact_name' => $this->nullableText($validated['contact_name'] ?? $conversation->contact_name),
            'contact_handle' => $this->nullableText($validated['contact_handle'] ?? $conversation->contact_handle),
            'status' => (string) $validated['status'],
            'user_id' => $validated['user_id'] ?? $conversation->user_id,
            'contact_id' => $validated['contact_id'] ?? $conversation->contact_id,
            'meta' => $validated['meta'] ?? $conversation->meta,
        ];

        if (! AccessControl::isElevated($request->user())) {
            $canAcceptIncoming = MessengerAccess::canAcceptIncoming($request->user(), $settings);
            if (! $canAcceptIncoming || (int) ($validated['user_id'] ?? 0) !== (int) $request->user()->id) {
                $payload['user_id'] = $conversation->user_id;
            }
        }

        if ($payload['external_id']) {
            $exists = MessengerConversation::query()
                ->where('provider', $provider)
                ->where('external_id', $payload['external_id'])
                ->whereKeyNot($conversation->id)
                ->exists();

            if ($exists) {
                abort(422, 'Conversation external ID must be unique for this provider.');
            }
        }

        if ($payload['contact_id']) {
            $contact = Contact::query()->find($payload['contact_id']);
            if ($contact) {
                $payload['contact_name'] = trim((string) $contact->full_name) !== '' ? trim((string) $contact->full_name) : $contact->first_name;
                $payload['contact_handle'] = $payload['contact_handle'] ?: ($contact->email ?: $contact->phone);
            }
        }

        $payload = $moduleManager->applyPayloadHooks('messengers.conversations.update', $payload, [
            'hook' => 'messengers.conversations.update',
            'user_id' => $request->user()->id,
            'conversation_id' => $conversation->id,
            'source' => 'api',
        ], array_keys($payload));

        $conversation->update($payload);

        return MessengerConversationResource::make($conversation->load(['channel', 'user', 'contact', 'lastMessage'])->loadCount('messages'));
    }

    public function accept(Request $request, MessengerConversation $conversation): MessengerConversationResource
    {
        $this->authorize('update', $conversation);

        $actor = $request->user();
        $settings = MessengerSetting::query()->first();
        $isElevated = AccessControl::isElevated($actor);

        $validated = $request->validate([
            'user_id' => ['nullable', 'integer', 'exists:users,id'],
        ]);

        if (! $isElevated) {
            abort_unless(MessengerAccess::canAcceptIncoming($actor, $settings), 403);
            if ($conversation->user_id !== null && (int) $conversation->user_id !== (int) $actor->id) {
                abort(403);
            }
        }

        $targetUserId = $isElevated
            ? (int) ($validated['user_id'] ?? $actor->id)
            : (int) $actor->id;

        $conversation->update([
            'user_id' => $targetUserId,
            'status' => $conversation->status === 'closed' ? 'open' : $conversation->status,
        ]);

        return MessengerConversationResource::make($conversation->load(['channel', 'user', 'contact', 'lastMessage'])->loadCount('messages'));
    }

    public function destroy(MessengerConversation $conversation)
    {
        $conversation->delete();

        return response()->noContent();
    }

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

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

