<?php

namespace App\Support\RightRail;

use App\Models\ChatMessage;
use App\Models\Disk;
use App\Models\User;
use App\Support\ProfileAvatarManager;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

class ChatProvider
{
    /**
     * @return Collection<int, array<string, mixed>>
     */
    public function chats(User $user): Collection
    {
        $currentUserId = (int) $user->id;
        $counterpartExpression = "CASE WHEN sender_id = {$currentUserId} THEN recipient_id ELSE sender_id END";

        $conversationStats = ChatMessage::query()
            ->where(function ($query) use ($currentUserId): void {
                $query->where('sender_id', $currentUserId)
                    ->orWhere('recipient_id', $currentUserId);
            })
            ->selectRaw("{$counterpartExpression} as counterpart_id")
            ->selectRaw('MAX(id) as last_message_id')
            ->selectRaw('MAX(created_at) as last_message_at')
            ->groupByRaw($counterpartExpression)
            ->get();

        $conversationByUserId = $conversationStats->keyBy(fn ($row): int => (int) $row->counterpart_id);

        $latestMessageById = ChatMessage::query()
            ->whereIn('id', $conversationStats->pluck('last_message_id')->filter()->all())
            ->get(['id', 'sender_id', 'recipient_id', 'body', 'disk_id', 'created_at'])
            ->keyBy('id');

        $diskNamesById = Disk::query()
            ->whereIn('id', $latestMessageById->pluck('disk_id')->filter()->all())
            ->pluck('name', 'id');

        $unreadCountByUserId = ChatMessage::query()
            ->where('recipient_id', $currentUserId)
            ->whereNull('read_at')
            ->selectRaw('sender_id, count(*) as unread_count')
            ->groupBy('sender_id')
            ->pluck('unread_count', 'sender_id');

        return User::query()
            ->whereKeyNot($user->id)
            ->orderBy('name')
            ->limit(50)
            ->get([
                'id',
                'name',
                'email',
                'role',
                'last_seen_at',
                'profile_photo_path',
                'profile_photo_focus_x',
                'profile_photo_focus_y',
                'profile_photo_zoom',
            ])
            ->map(function (User $member) use (
                $conversationByUserId,
                $latestMessageById,
                $unreadCountByUserId,
                $diskNamesById
            ): array {
                $isOnline = $member->last_seen_at instanceof Carbon
                    && $member->last_seen_at->gte(now()->subMinutes(5));

                $memberConversation = $conversationByUserId->get((int) $member->id);
                $lastMessage = null;
                if ($memberConversation !== null) {
                    $lastMessage = $latestMessageById->get((int) ($memberConversation->last_message_id ?? 0));
                }

                $lastMessageAt = $lastMessage?->created_at;
                $unreadCount = min((int) ($unreadCountByUserId[$member->id] ?? 0), 99);
                $lastMessageBody = trim((string) ($lastMessage?->body ?? ''));
                $attachmentName = (string) ($diskNamesById[(int) ($lastMessage->disk_id ?? 0)] ?? '');
                $lastMessagePreview = $lastMessageBody !== ''
                    ? $lastMessageBody
                    : (($lastMessage && (int) ($lastMessage->disk_id ?? 0) > 0)
                        ? __('File').': '.($attachmentName !== '' ? $attachmentName : '#'.(int) $lastMessage->disk_id)
                        : '');

                return [
                    'id' => $member->id,
                    'name' => $member->name,
                    'email' => $member->email,
                    'role_label' => $this->roleLabel((string) $member->role),
                    'initials' => $this->initials((string) $member->name),
                    '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
                    ),
                    'is_online' => $isOnline,
                    'last_seen' => $isOnline
                        ? 'online'
                        : ($member->last_seen_at?->diffForHumans() ?? 'no data'),
                    'last_message' => $lastMessage
                        ? Str::limit((string) $lastMessagePreview, 60)
                        : 'No messages',
                    'last_message_at' => $lastMessageAt?->toIso8601String(),
                    'last_message_human' => $lastMessageAt?->diffForHumans() ?? '—',
                    'last_message_timestamp' => $lastMessageAt?->timestamp ?? 0,
                    'unread_count' => $unreadCount,
                ];
            })
            ->sort(function (array $left, array $right): int {
                $unreadDiff = (int) ($right['unread_count'] ?? 0) <=> (int) ($left['unread_count'] ?? 0);
                if ($unreadDiff !== 0) {
                    return $unreadDiff;
                }

                $timestampDiff = (int) ($right['last_message_timestamp'] ?? 0) <=> (int) ($left['last_message_timestamp'] ?? 0);
                if ($timestampDiff !== 0) {
                    return $timestampDiff;
                }

                if (($left['is_online'] ?? false) !== ($right['is_online'] ?? false)) {
                    return ($right['is_online'] ?? false) <=> ($left['is_online'] ?? false);
                }

                return strcasecmp((string) ($left['name'] ?? ''), (string) ($right['name'] ?? ''));
            })
            ->values()
            ->map(function (array $chat): array {
                unset($chat['last_message_timestamp']);

                return $chat;
            });
    }

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

    private function initials(string $name): string
    {
        $parts = preg_split('/\s+/u', trim($name)) ?: [];
        $first = $parts[0] ?? '';
        $last = $parts[1] ?? '';
        $initials = mb_strtoupper(mb_substr($first, 0, 1).mb_substr($last, 0, 1));

        if ($initials === '') {
            $initials = mb_strtoupper(mb_substr($name, 0, 1));
        }

        return $initials;
    }
}
