<?php

namespace App\Http\Controllers;

use App\Models\ChatMessage;
use App\Models\Disk;
use App\Models\User;
use App\Support\AccessControl;
use App\Support\DiskFileIconResolver;
use App\Support\DiskFileManager;
use App\Support\ProfileAvatarManager;
use App\Support\RightRailManager;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class ChatConversationController extends Controller
{
    public function sidebar(Request $request, User $member, RightRailManager $rightRailManager): View
    {
        $user = $request->user();
        if ((int) $member->id === (int) $user->id) {
            abort(422, 'You cannot open a chat with yourself.');
        }

        $chats = collect($rightRailManager->data($user)['chats'] ?? [])->values();
        $hasMemberChat = $chats->contains(fn (array $chat): bool => (int) ($chat['id'] ?? 0) === (int) $member->id);
        if (! $hasMemberChat) {
            abort(404);
        }

        $data = [
            'chats' => $chats,
            'initialChatId' => (int) $member->id,
            'conversationShowUrlTemplate' => route('chat.conversations.show', ['member' => '__MEMBER__'], false),
            'conversationStoreUrlTemplate' => route('chat.conversations.store', ['member' => '__MEMBER__'], false),
            'diskFiles' => $this->diskFilesPayload($user),
        ];

        if ($request->boolean('sidepanel') || $request->header('X-Sidepanel') === '1') {
            return view('sidepanel.chats.index', $data);
        }

        return view('chats.index', $data);
    }

    public function show(Request $request, User $member): JsonResponse
    {
        $user = $request->user();
        if ((int) $member->id === (int) $user->id) {
            return response()->json([
                'message' => "You can't open a chat with yourself.",
            ], 422);
        }

        ChatMessage::query()
            ->where('sender_id', $member->id)
            ->where('recipient_id', $user->id)
            ->whereNull('read_at')
            ->update(['read_at' => now()]);

        $messages = ChatMessage::query()
            ->with('disk:id,name,extension,mime_type')
            ->where(function ($query) use ($user, $member): void {
                $query->where(function ($direct) use ($user, $member): void {
                    $direct->where('sender_id', $user->id)
                        ->where('recipient_id', $member->id);
                })->orWhere(function ($reverse) use ($user, $member): void {
                    $reverse->where('sender_id', $member->id)
                        ->where('recipient_id', $user->id);
                });
            })
            ->latest('id')
            ->limit(120)
            ->get(['id', 'sender_id', 'recipient_id', 'body', 'disk_id', 'read_at', 'created_at'])
            ->sortBy('id')
            ->values()
            ->map(fn (ChatMessage $message): array => $this->messagePayload($message, $user))
            ->all();

        return response()->json([
            'member' => $this->memberPayload($member),
            'messages' => $messages,
        ]);
    }

    public function store(Request $request, User $member): JsonResponse
    {
        $user = $request->user();
        if ((int) $member->id === (int) $user->id) {
            return response()->json([
                'message' => "You can't send a message to yourself.",
            ], 422);
        }

        $validated = $request->validate([
            'body' => ['nullable', 'string', 'max:5000', 'required_without:disk_id'],
            'disk_id' => ['nullable', 'integer', 'exists:disks,id', 'required_without:body'],
        ]);

        $disk = null;
        $diskId = (int) ($validated['disk_id'] ?? 0);
        if ($diskId > 0) {
            $disk = Disk::query()->find($diskId);
            if (! $disk instanceof Disk || ! $user->can('view', $disk)) {
                return response()->json([
                    'message' => __('The selected disk file is not available.'),
                ], 403);
            }
        }

        $body = trim((string) ($validated['body'] ?? ''));
        $message = ChatMessage::query()->create([
            'sender_id' => $user->id,
            'recipient_id' => $member->id,
            'body' => $body,
            'disk_id' => $disk?->id,
            'read_at' => null,
        ]);

        $message->setRelation('disk', $disk);

        return response()->json([
            'message' => $this->messagePayload($message, $user),
        ], 201);
    }

    /**
     * @return array<string, mixed>
     */
    private function memberPayload(User $member): array
    {
        $isOnline = $member->last_seen_at !== null
            && $member->last_seen_at->gte(now()->subMinutes(5));

        return [
            'id' => $member->id,
            'name' => $member->name,
            'role_label' => $this->roleLabel((string) $member->role),
            'is_online' => $isOnline,
            'last_seen' => $isOnline
                ? 'online'
                : ($member->last_seen_at?->diffForHumans() ?? 'no data'),
            'photo_url' => ProfileAvatarManager::url($member->profile_photo_path),
            'photo_style' => ProfileAvatarManager::style(
                $member->profile_photo_focus_x,
                $member->profile_photo_focus_y,
                $member->profile_photo_zoom
            ),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function messagePayload(ChatMessage $message, User $currentUser): array
    {
        $body = trim((string) $message->body);
        $disk = $message->disk;
        $diskAttachment = null;
        $previewText = $body;

        if ($disk instanceof Disk) {
            $previewUrl = app(DiskFileManager::class)->canPreviewInCrm($disk)
                ? route('disks.preview', ['disk' => $disk])
                : route('disks.download', ['disk' => $disk]);

            $diskAttachment = [
                'id' => (int) $disk->id,
                'name' => (string) $disk->name,
                'extension' => (string) ($disk->extension ?? ''),
                'icon' => DiskFileIconResolver::resolve((string) ($disk->extension ?? ''), (string) ($disk->mime_type ?? ''))['icon'],
                'preview_url' => $previewUrl,
                'download_url' => route('disks.download', ['disk' => $disk]),
            ];

            if ($previewText === '') {
                $previewText = __('File').': '.$disk->name;
            }
        }

        return [
            'id' => $message->id,
            'body' => $body,
            'preview_text' => $previewText,
            'disk_attachment' => $diskAttachment,
            'is_own' => (int) $message->sender_id === (int) $currentUser->id,
            'is_read' => $message->read_at !== null,
            'read_at' => $message->read_at?->toIso8601String(),
            'sent_at' => $message->created_at?->toIso8601String(),
            'sent_at_human' => $message->created_at?->diffForHumans() ?? '',
            'sent_at_time' => $message->created_at?->format('H:i') ?? '',
        ];
    }

    /**
     * @return array<int, array{id:int,name:string,folder:string,extension:string,icon:string,owner_name:string,preview_url:string,download_url:string}>
     */
    private function diskFilesPayload(User $user): array
    {
        if (! AccessControl::allows($user, 'disks', 'read')) {
            return [];
        }

        $query = Disk::query()
            ->with('owner:id,name')
            ->select(['id', 'name', 'folder', 'extension', 'mime_type', 'owner_id', 'updated_at']);

        if (! AccessControl::isElevated($user)) {
            $query->where('owner_id', $user->id);
        }

        $manager = app(DiskFileManager::class);

        return $query
            ->latest('updated_at')
            ->limit(100)
            ->get()
            ->map(function (Disk $disk) use ($manager): array {
                return [
                    'id' => (int) $disk->id,
                    'name' => (string) $disk->name,
                    'folder' => (string) ($disk->folder ?? ''),
                    'extension' => (string) ($disk->extension ?? ''),
                    'icon' => DiskFileIconResolver::resolve((string) ($disk->extension ?? ''), (string) ($disk->mime_type ?? ''))['icon'],
                    'owner_name' => (string) ($disk->owner?->name ?? ''),
                    'preview_url' => $manager->canPreviewInCrm($disk)
                        ? route('disks.preview', ['disk' => $disk])
                        : route('disks.download', ['disk' => $disk]),
                    'download_url' => route('disks.download', ['disk' => $disk]),
                ];
            })
            ->values()
            ->all();
    }

    private function roleLabel(string $role): string
    {
        return match ($role) {
            'admin' => 'Administrator',
            'moderator', 'manager' => 'Moderator',
            'user', 'sales' => 'User',
            default => $role,
        };
    }
}
