<?php

/**
 * 文件功能：游戏房间范围配置服务
 *
 * 统一解析所有游戏的 room_scope_mode 与 room_ids 配置，
 * 供后台保存、调度任务、前台准入校验和公共回合查询复用。
 */

namespace App\Services;

use App\Models\GameConfig;
use App\Models\User;
use Illuminate\Http\Request;

/**
 * 类功能：统一管理所有游戏的房间范围读取与房间判定。
 */
class GameRoomScopeService
{
    /**
     * 房间模式常量：全部房间。
     */
    public const MODE_ALL = 'all';

    /**
     * 房间模式常量：单选房间。
     */
    public const MODE_SINGLE = 'single';

    /**
     * 房间模式常量：多选房间。
     */
    public const MODE_MULTIPLE = 'multiple';

    /**
     * 支持的房间模式列表。
     *
     * @var array<int, string>
     */
    public const SUPPORTED_MODES = [
        self::MODE_ALL,
        self::MODE_SINGLE,
        self::MODE_MULTIPLE,
    ];

    /**
     * 构造房间范围服务。
     */
    public function __construct(
        private readonly ChatStateService $chatState,
    ) {}

    /**
     * 归一化房间模式。
     */
    public function normalizeRoomScopeMode(?string $mode, string $default = self::MODE_SINGLE): string
    {
        $normalizedMode = (string) $mode;

        if (! in_array($normalizedMode, self::SUPPORTED_MODES, true)) {
            return $default;
        }

        return $normalizedMode;
    }

    /**
     * 把原始房间数组归一化为去重后的整型数组。
     *
     * @return array<int, int>
     */
    public function normalizeRoomIds(mixed $roomIds, array $default = [1]): array
    {
        $items = is_array($roomIds)
            ? $roomIds
            : preg_split('/[\s,，]+/u', (string) $roomIds, -1, PREG_SPLIT_NO_EMPTY);

        $normalizedRoomIds = collect($items)
            ->map(fn (mixed $roomId): int => (int) $roomId)
            ->filter(fn (int $roomId): bool => $roomId > 0)
            ->unique()
            ->values()
            ->all();

        if ($normalizedRoomIds === []) {
            return $default;
        }

        return $normalizedRoomIds;
    }

    /**
     * 从 params 数组中解析房间范围配置。
     *
     * @return array{room_scope_mode:string,room_ids:array<int, int>}
     */
    public function getScopeConfigForParams(array $params, array $defaultRoomIds = [1]): array
    {
        if (
            ! array_key_exists('room_scope_mode', $params)
            && ! array_key_exists('room_ids', $params)
            && ! array_key_exists('room_id', $params)
        ) {
            return [
                'room_scope_mode' => self::MODE_ALL,
                'room_ids' => $this->normalizeRoomIds($defaultRoomIds, [1]),
            ];
        }

        $roomScopeMode = $this->normalizeRoomScopeMode(
            mode: (string) ($params['room_scope_mode'] ?? self::MODE_SINGLE),
            default: self::MODE_SINGLE,
        );

        $roomIds = $this->normalizeRoomIds(
            roomIds: $params['room_ids'] ?? (($params['room_id'] ?? null) !== null ? [$params['room_id']] : []),
            default: $defaultRoomIds,
        );

        return [
            'room_scope_mode' => $roomScopeMode,
            'room_ids' => $roomIds,
        ];
    }

    /**
     * 读取指定游戏当前配置中的房间范围。
     *
     * @return array{room_scope_mode:string,room_ids:array<int, int>}
     */
    public function getScopeConfigForGame(string $gameKey, array $defaultRoomIds = [1]): array
    {
        $params = GameConfig::forGame($gameKey)?->params ?? [];

        return $this->getScopeConfigForParams($params, $defaultRoomIds);
    }

    /**
     * 获取指定游戏真正生效的房间 ID 列表。
     *
     * @return array<int, int>
     */
    public function getScopedRoomIdsForGame(string $gameKey, array $defaultRoomIds = [1]): array
    {
        $scopeConfig = $this->getScopeConfigForGame($gameKey, $defaultRoomIds);

        if ($scopeConfig['room_scope_mode'] === self::MODE_ALL) {
            return $this->resolveAllAvailableRoomIds($defaultRoomIds);
        }

        return $scopeConfig['room_ids'];
    }

    /**
     * 获取指定游戏的首选房间。
     */
    public function getPrimaryRoomIdForGame(string $gameKey, int $fallback = 1): int
    {
        $roomIds = $this->getScopedRoomIdsForGame($gameKey, [$fallback]);

        return $roomIds[0] ?? $fallback;
    }

    /**
     * 判断某个房间是否在指定游戏允许范围内。
     */
    public function isRoomAllowedForGame(string $gameKey, int $roomId, array $defaultRoomIds = [1]): bool
    {
        return in_array($roomId, $this->getScopedRoomIdsForGame($gameKey, $defaultRoomIds), true);
    }

    /**
     * 从请求或在线状态解析当前操作房间。
     */
    public function resolveRequestRoomId(Request $request, ?User $user = null, int $fallback = 1): int
    {
        $requestedRoomId = (int) $request->integer('room_id', 0);
        if ($requestedRoomId > 0) {
            return $requestedRoomId;
        }

        return $this->resolveUserRoomId($user ?? $request->user(), $fallback);
    }

    /**
     * 从用户在线房间或用户资料中推断当前房间。
     */
    public function resolveUserRoomId(?User $user, int $fallback = 1): int
    {
        if (! $user) {
            return $fallback;
        }

        $activeRoomIds = $this->chatState->getUserRooms($user->username);
        if ($activeRoomIds !== []) {
            return (int) $activeRoomIds[0];
        }

        $profileRoomId = (int) ($user->room_id ?? 0);

        return $profileRoomId > 0 ? $profileRoomId : $fallback;
    }

    /**
     * 返回通用后台复用的默认房间范围配置。
     *
     * @return array{room_scope_mode:string,room_ids:array<int, int>}
     */
    public function defaultScopeConfig(array $defaultRoomIds = [1]): array
    {
        return [
            'room_scope_mode' => self::MODE_SINGLE,
            'room_ids' => $this->normalizeRoomIds($defaultRoomIds, [1]),
        ];
    }

    /**
     * 在“全部房间”模式下解析当前可用房间。
     *
     * @return array<int, int>
     */
    private function resolveAllAvailableRoomIds(array $defaultRoomIds = [1]): array
    {
        $roomIds = \App\Models\Room::query()
            ->orderBy('id')
            ->pluck('id')
            ->map(fn (mixed $roomId): int => (int) $roomId)
            ->all();

        return $roomIds !== [] ? $roomIds : $defaultRoomIds;
    }
}
