<?php

namespace App\Http\Controllers\admin;

use App\Constants\HttpStatus;
use App\Http\Controllers\Controller;
use App\Models\EgameItem;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

class Egame extends Controller
{
    private const FALLBACK_GAME_TYPE_NAMES = [
        1 => '视讯',
        2 => '电子',
        3 => '彩票',
        4 => '体育',
        5 => '电竞',
        6 => '捕鱼',
        7 => '棋牌',
    ];

    public function items(): JsonResponse
    {
        try {
            request()->validate([
                'item_type' => ['nullable', Rule::in([
                    EgameItem::TYPE_CATEGORY,
                    EgameItem::TYPE_PLATFORM,
                    EgameItem::TYPE_GAME,
                ])],
                'platform_scope' => ['nullable', Rule::in([
                    EgameItem::PLATFORM_SCOPE_GLOBAL,
                    EgameItem::PLATFORM_SCOPE_CATEGORY,
                ])],
                'plat_type' => ['nullable', 'string', 'max:32'],
                'game_type' => ['nullable', 'integer', 'min:0', 'max:255'],
                'game_code' => ['nullable', 'string', 'max:128'],
                'keyword' => ['nullable', 'string', 'max:128'],
                'status' => ['nullable', 'integer', Rule::in([
                    EgameItem::STATUS_DISABLED,
                    EgameItem::STATUS_ENABLED,
                ])],
                'page' => ['nullable', 'integer', 'min:1'],
                'limit' => ['nullable', 'integer', 'min:1', 'max:200'],
            ]);

            $query = EgameItem::query();
            foreach (['item_type', 'plat_type', 'game_type', 'game_code', 'status'] as $field) {
                $value = request()->input($field);
                if ($value !== null && $value !== '') {
                    $query->where($field, $value);
                }
            }

            $platformScope = (string) request()->input('platform_scope', '');
            if ($platformScope !== '') {
                $itemType = (string) request()->input('item_type', '');
                if ($itemType !== '' && $itemType !== EgameItem::TYPE_PLATFORM) {
                    throw new Exception('platform_scope 只适用于平台配置', HttpStatus::CUSTOM_ERROR);
                }
                $query->where('item_type', EgameItem::TYPE_PLATFORM);
                if ($platformScope === EgameItem::PLATFORM_SCOPE_GLOBAL) {
                    $query->where('game_type', 0);
                } else {
                    $query->where('game_type', '>', 0);
                }
            }

            $keyword = trim((string) request()->input('keyword', ''));
            if ($keyword !== '') {
                $query->where(function ($query) use ($keyword) {
                    $query->where('name', 'like', "%{$keyword}%")
                        ->orWhere('plat_type', 'like', "%{$keyword}%")
                        ->orWhere('game_code', 'like', "%{$keyword}%");
                });
            }

            $limit = (int) request()->input('limit', 15);
            $page = (int) request()->input('page', 1);
            $total = (clone $query)->count();
            $states = $this->catalogStates();
            $list = $query->orderByDesc('sort')
                ->orderByDesc('id')
                ->forPage($page, $limit)
                ->get()
                ->map(fn (EgameItem $item) => $this->decorateItem($item, $states))
                ->values();
        } catch (ValidationException $e) {
            return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
        } catch (Exception $e) {
            return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage());
        }

        return $this->success(['total' => $total, 'data' => $list]);
    }

    public function options(): JsonResponse
    {
        try {
            $states = $this->catalogStates();
            $categoryPlatformCounts = $this->groupedCounts(
                EgameItem::query()
                    ->where('item_type', EgameItem::TYPE_PLATFORM)
                    ->where('game_type', '>', 0),
                'game_type',
                'plat_type'
            );
            $categoryGameCounts = $this->groupedCounts(
                EgameItem::query()->where('item_type', EgameItem::TYPE_GAME),
                'game_type',
                'id'
            );
            $platformCategoryCounts = $this->groupedCounts(
                EgameItem::query()
                    ->where('item_type', EgameItem::TYPE_PLATFORM)
                    ->where('game_type', '>', 0),
                'plat_type',
                'game_type'
            );
            $platformGameCounts = $this->groupedCounts(
                EgameItem::query()->where('item_type', EgameItem::TYPE_GAME),
                'plat_type',
                'id'
            );

            $categories = EgameItem::query()
                ->where('item_type', EgameItem::TYPE_CATEGORY)
                ->orderByDesc('sort')
                ->orderBy('game_type')
                ->get()
                ->map(function (EgameItem $item) use ($states, $categoryPlatformCounts, $categoryGameCounts) {
                    $this->decorateItem($item, $states);
                    $key = (string) $item->game_type;
                    $item->setAttribute('platform_count', $categoryPlatformCounts[$key] ?? 0);
                    $item->setAttribute('game_count', $categoryGameCounts[$key] ?? 0);
                    return $item;
                })
                ->values();

            $platforms = EgameItem::query()
                ->where('item_type', EgameItem::TYPE_PLATFORM)
                ->where('game_type', 0)
                ->orderByDesc('sort')
                ->orderBy('name')
                ->orderBy('plat_type')
                ->get()
                ->map(function (EgameItem $item) use ($states, $platformCategoryCounts, $platformGameCounts) {
                    $this->decorateItem($item, $states);
                    $key = strtolower((string) $item->plat_type);
                    $item->setAttribute('category_count', $platformCategoryCounts[$key] ?? 0);
                    $item->setAttribute('game_count', $platformGameCounts[$key] ?? 0);
                    return $item;
                })
                ->values();
        } catch (Exception $e) {
            return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage());
        }

        return $this->success([
            'categories' => $categories,
            'platforms' => $platforms,
            'statuses' => [
                ['value' => EgameItem::STATUS_ENABLED, 'label' => '开启'],
                ['value' => EgameItem::STATUS_DISABLED, 'label' => '关闭'],
            ],
            'ingress' => [
                ['value' => '1', 'label' => '仅电脑'],
                ['value' => '2', 'label' => '仅手机'],
                ['value' => '3', 'label' => '通用'],
            ],
        ]);
    }

    public function update(): JsonResponse
    {
        try {
            request()->validate([
                'id' => ['nullable', 'integer', 'min:0'],
                'item_type' => ['nullable', Rule::in([
                    EgameItem::TYPE_CATEGORY,
                    EgameItem::TYPE_PLATFORM,
                    EgameItem::TYPE_GAME,
                ])],
                'platform_scope' => ['nullable', Rule::in([
                    EgameItem::PLATFORM_SCOPE_GLOBAL,
                    EgameItem::PLATFORM_SCOPE_CATEGORY,
                ])],
                'plat_type' => ['nullable', 'string', 'max:32'],
                'game_type' => ['nullable', 'integer', 'min:0', 'max:255'],
                'game_code' => ['nullable', 'string', 'max:128'],
                'name' => ['nullable', 'string', 'max:128'],
                'ingress' => ['nullable', Rule::in(['1', '2', '3'])],
                'lobby_enabled' => ['nullable', 'boolean'],
                'logo' => ['nullable', 'string', 'max:500'],
                'logo_langs' => ['nullable', 'array'],
                'status' => ['nullable', 'integer', Rule::in([
                    EgameItem::STATUS_DISABLED,
                    EgameItem::STATUS_ENABLED,
                ])],
                'sort' => ['nullable', 'integer', 'min:-999999', 'max:999999'],
            ]);

            $id = (int) request()->input('id', 0);
            $item = DB::transaction(function () use ($id) {
                if ($id > 0) {
                    $item = EgameItem::query()->find($id);
                    if (!$item) {
                        throw new Exception('配置不存在', HttpStatus::CUSTOM_ERROR);
                    }
                    $this->assertIdentityUnchanged($item);
                } else {
                    $identity = $this->normalizedIdentityForCreate();
                    $item = EgameItem::query()->firstOrNew($identity);
                }

                $data = $this->mutableItemData($item, !$item->exists);
                $name = array_key_exists('name', $data) ? $data['name'] : (string) $item->name;
                if ($item->isCategory() && trim($name) === '') {
                    throw new Exception('分类名称不能为空', HttpStatus::CUSTOM_ERROR);
                }

                if (!empty($data) || !$item->exists) {
                    $item->fill($data)->save();
                }

                return $item->fresh();
            });
        } catch (ValidationException $e) {
            return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
        } catch (Exception $e) {
            return $this->error((int) $e->getCode(), $e->getMessage());
        }

        return $this->success($this->decorateItem($item, $this->catalogStates()));
    }

    public function setStatus(): JsonResponse
    {
        try {
            request()->validate([
                'id' => ['required', 'integer', 'min:1'],
                'status' => ['required', 'integer', Rule::in([
                    EgameItem::STATUS_DISABLED,
                    EgameItem::STATUS_ENABLED,
                ])],
            ]);

            $item = EgameItem::query()->find((int) request()->input('id'));
            if (!$item) {
                throw new Exception('配置不存在', HttpStatus::CUSTOM_ERROR);
            }
            $item->status = (int) request()->input('status');
            $item->save();
            $item = $item->fresh();
        } catch (ValidationException $e) {
            return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
        } catch (Exception $e) {
            return $this->error((int) $e->getCode(), $e->getMessage());
        }

        return $this->success($this->decorateItem($item, $this->catalogStates()));
    }

    public function delete(): JsonResponse
    {
        try {
            request()->validate([
                'id' => ['required', 'integer', 'min:1'],
            ]);

            if (!EgameItem::query()->whereKey((int) request()->input('id'))->exists()) {
                throw new Exception('配置不存在', HttpStatus::CUSTOM_ERROR);
            }
        } catch (ValidationException $e) {
            return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
        } catch (Exception $e) {
            return $this->error((int) $e->getCode(), $e->getMessage());
        }

        return $this->error(
            HttpStatus::CUSTOM_ERROR,
            '第三方游戏目录由同步任务维护，不能删除，请将状态设为关闭'
        );
    }

    private function normalizedIdentityForCreate(): array
    {
        $input = request()->all();
        if (!array_key_exists('item_type', $input) || !array_key_exists('game_type', $input)) {
            throw new Exception('新增配置必须填写 item_type 和 game_type', HttpStatus::CUSTOM_ERROR);
        }

        $itemType = (string) request()->input('item_type', '');
        $platformScope = (string) request()->input('platform_scope', '');
        if (!in_array($itemType, [
            EgameItem::TYPE_CATEGORY,
            EgameItem::TYPE_PLATFORM,
            EgameItem::TYPE_GAME,
        ], true)) {
            throw new Exception('item_type 参数错误', HttpStatus::CUSTOM_ERROR);
        }
        if ($platformScope !== '' && $itemType !== EgameItem::TYPE_PLATFORM) {
            throw new Exception('platform_scope 只适用于平台配置', HttpStatus::CUSTOM_ERROR);
        }

        $identity = [
            'item_type' => $itemType,
            'plat_type' => strtolower(trim((string) request()->input('plat_type', ''))),
            'game_type' => (int) request()->input('game_type'),
            'game_code' => trim((string) request()->input('game_code', '')),
        ];

        if ($itemType === EgameItem::TYPE_CATEGORY) {
            if ($identity['game_type'] <= 0) {
                throw new Exception('分类 game_type 必须大于 0', HttpStatus::CUSTOM_ERROR);
            }
            $identity['plat_type'] = '';
            $identity['game_code'] = '';
            return $identity;
        }

        if ($identity['plat_type'] === '') {
            throw new Exception('平台代码不能为空', HttpStatus::CUSTOM_ERROR);
        }

        if ($itemType === EgameItem::TYPE_PLATFORM) {
            if ($platformScope === EgameItem::PLATFORM_SCOPE_GLOBAL) {
                $identity['game_type'] = 0;
            }
            if ($platformScope === EgameItem::PLATFORM_SCOPE_CATEGORY && $identity['game_type'] <= 0) {
                throw new Exception('平台分类关联的 game_type 必须大于 0', HttpStatus::CUSTOM_ERROR);
            }
            $identity['game_code'] = '';
            return $identity;
        }

        if ($identity['game_type'] <= 0) {
            throw new Exception('游戏 game_type 必须大于 0', HttpStatus::CUSTOM_ERROR);
        }
        if ($identity['game_code'] === '') {
            throw new Exception('游戏配置必须填写 game_code', HttpStatus::CUSTOM_ERROR);
        }

        return $identity;
    }

    private function assertIdentityUnchanged(EgameItem $item): void
    {
        $input = request()->all();
        $checks = [
            'item_type' => (string) $item->item_type,
            'plat_type' => strtolower((string) $item->plat_type),
            'game_type' => (int) $item->game_type,
            'game_code' => (string) $item->game_code,
        ];

        foreach ($checks as $field => $expected) {
            if (!array_key_exists($field, $input) || $input[$field] === null) {
                continue;
            }
            $actual = $input[$field];
            if ($field === 'plat_type') {
                $actual = strtolower(trim((string) $actual));
            } elseif ($field === 'game_type') {
                $actual = (int) $actual;
            } else {
                $actual = trim((string) $actual);
            }
            if ($actual !== $expected) {
                throw new Exception('目录标识不允许修改：' . $field, HttpStatus::CUSTOM_ERROR);
            }
        }

        if (
            array_key_exists('platform_scope', $input)
            && $input['platform_scope'] !== null
            && $input['platform_scope'] !== ''
        ) {
            $expectedScope = '';
            if ($item->isGlobalPlatform()) {
                $expectedScope = EgameItem::PLATFORM_SCOPE_GLOBAL;
            } elseif ($item->isPlatformRelation()) {
                $expectedScope = EgameItem::PLATFORM_SCOPE_CATEGORY;
            }
            if ((string) $input['platform_scope'] !== $expectedScope) {
                throw new Exception('目录标识不允许修改：platform_scope', HttpStatus::CUSTOM_ERROR);
            }
        }
    }

    private function mutableItemData(EgameItem $item, bool $isNew): array
    {
        $input = request()->all();
        $data = $isNew ? [
            'name' => '',
            'ingress' => '3',
            'lobby_enabled' => 0,
            'logo' => '',
            'logo_langs' => null,
            'status' => EgameItem::STATUS_ENABLED,
            'sort' => 0,
        ] : [];

        foreach (['name', 'ingress', 'logo'] as $field) {
            if (array_key_exists($field, $input)) {
                $data[$field] = trim((string) $input[$field]);
            }
        }
        if (array_key_exists('logo_langs', $input)) {
            $data['logo_langs'] = $item->item_type === EgameItem::TYPE_GAME
                ? $input['logo_langs']
                : null;
        }
        if (array_key_exists('status', $input)) {
            $data['status'] = (int) $input['status'];
        }
        if (array_key_exists('sort', $input)) {
            $data['sort'] = (int) $input['sort'];
        }
        if (array_key_exists('lobby_enabled', $input)) {
            $data['lobby_enabled'] = $item->isPlatformRelation()
                ? (int) request()->boolean('lobby_enabled')
                : 0;
        }

        if ($item->item_type !== EgameItem::TYPE_GAME && array_key_exists('ingress', $data)) {
            $data['ingress'] = '3';
        }
        if ($item->item_type !== EgameItem::TYPE_GAME && array_key_exists('logo_langs', $data)) {
            $data['logo_langs'] = null;
        }
        if (!$item->isPlatformRelation() && array_key_exists('lobby_enabled', $data)) {
            $data['lobby_enabled'] = 0;
        }

        return $data;
    }

    private function catalogStates(): array
    {
        $states = [
            'categories' => [],
            'global_platforms' => [],
            'platform_relations' => [],
        ];

        $rows = EgameItem::query()
            ->whereIn('item_type', [EgameItem::TYPE_CATEGORY, EgameItem::TYPE_PLATFORM])
            ->get();

        foreach ($rows as $row) {
            if ($row->isCategory()) {
                $states['categories'][(string) $row->game_type] = $row;
                continue;
            }

            $platType = strtolower((string) $row->plat_type);
            if ($row->isGlobalPlatform()) {
                $states['global_platforms'][$platType] = $row;
                continue;
            }
            if ($row->isPlatformRelation()) {
                $states['platform_relations'][$platType][(string) $row->game_type] = $row;
            }
        }

        return $states;
    }

    private function decorateItem(EgameItem $item, array $states): EgameItem
    {
        $platType = strtolower((string) $item->plat_type);
        $gameType = (string) $item->game_type;
        $disabledBy = null;

        $category = $states['categories'][$gameType] ?? null;
        $globalPlatform = $states['global_platforms'][$platType] ?? null;
        $platformRelation = $states['platform_relations'][$platType][$gameType] ?? null;

        if ($item->isCategory()) {
            if (!$this->enabled($item)) {
                $disabledBy = 'category';
            }
        } elseif ($item->isGlobalPlatform()) {
            if (!$this->enabled($item)) {
                $disabledBy = 'platform';
            }
        } elseif ($item->isPlatformRelation()) {
            if (!$this->enabled($category)) {
                $disabledBy = 'category';
            } elseif (!$this->enabled($globalPlatform)) {
                $disabledBy = 'platform';
            } elseif (!$this->enabled($item)) {
                $disabledBy = 'platform_category';
            }
        } else {
            if (!$this->enabled($category)) {
                $disabledBy = 'category';
            } elseif (!$this->enabled($globalPlatform)) {
                $disabledBy = 'platform';
            } elseif (!$this->enabled($platformRelation)) {
                $disabledBy = 'platform_category';
            } elseif (!$this->enabled($item)) {
                $disabledBy = 'game';
            }
        }

        $item->setAttribute('game_type_name', $this->gameTypeName((int) $item->game_type, $states));
        $item->setAttribute(
            'platform_name',
            $this->platformName($item, $globalPlatform, $platformRelation)
        );
        if ($item->item_type === EgameItem::TYPE_PLATFORM) {
            $item->setAttribute(
                'platform_scope',
                $item->isGlobalPlatform()
                    ? EgameItem::PLATFORM_SCOPE_GLOBAL
                    : EgameItem::PLATFORM_SCOPE_CATEGORY
            );
        } else {
            $item->setAttribute('platform_scope', null);
        }
        $item->setAttribute('effective_status', $disabledBy === null ? 1 : 0);
        $item->setAttribute('disabled_by', $disabledBy);

        return $item;
    }

    private function enabled(?EgameItem $item): bool
    {
        return $item !== null && $item->status === EgameItem::STATUS_ENABLED;
    }

    private function platformName(
        EgameItem $item,
        ?EgameItem $globalPlatform,
        ?EgameItem $platformRelation
    ): string {
        if ($item->isCategory()) {
            return '';
        }
        if ($item->isGlobalPlatform() && trim((string) $item->name) !== '') {
            return (string) $item->name;
        }
        if ($globalPlatform && trim((string) $globalPlatform->name) !== '') {
            return (string) $globalPlatform->name;
        }
        if ($platformRelation && trim((string) $platformRelation->name) !== '') {
            return (string) $platformRelation->name;
        }
        if ($item->item_type === EgameItem::TYPE_PLATFORM && trim((string) $item->name) !== '') {
            return (string) $item->name;
        }

        return (string) $item->plat_type;
    }

    private function gameTypeName(int $gameType, array $states): string
    {
        if ($gameType <= 0) {
            return '';
        }

        $category = $states['categories'][(string) $gameType] ?? null;
        if ($category && trim((string) $category->name) !== '') {
            return (string) $category->name;
        }

        return self::FALLBACK_GAME_TYPE_NAMES[$gameType] ?? ('分类' . $gameType);
    }

    private function groupedCounts($query, string $groupColumn, string $countColumn): array
    {
        return $query
            ->select($groupColumn)
            ->selectRaw("COUNT(DISTINCT {$countColumn}) AS aggregate")
            ->groupBy($groupColumn)
            ->pluck('aggregate', $groupColumn)
            ->map(fn ($count) => (int) $count)
            ->all();
    }
}
