<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
namespace app\dice\logic\reward;

use app\dice\model\reward\DiceReward;
use app\dice\model\reward_config\DiceRewardConfig;
use plugin\saiadmin\exception\ApiException;
use support\think\Db;

/**
 * 奖励对照逻辑层（DiceReward）
 * 权重 1-10000，区分顺时针/逆时针，修改后刷新 DiceReward 缓存
 */
class DiceRewardLogic
{
    private const WEIGHT_MIN = 1;
    private const WEIGHT_MAX = 10000;

    /** 档位键 */
    private const TIER_KEYS = ['T1', 'T2', 'T3', 'T4', 'T5', 'BIGWIN'];

    /**
     * 分页列表（按方向筛选，关联 dice_reward_config 展示 grid_number、ui_text、real_ev、remark）
     * @param int $direction 0=顺时针 1=逆时针
     * @param array{tier?: string, orderField?: string, orderType?: string} $where tier 档位筛选
     * @param int $page
     * @param int $limit
     * @return array{total: int, per_page: int, current_page: int, data: array}
     */
    public function getListWithConfig(int $direction, array $where, int $page = 1, int $limit = 10): array
    {
        $tier = isset($where['tier']) ? trim((string) $where['tier']) : '';
        $orderField = isset($where['orderField']) && $where['orderField'] !== '' ? (string) $where['orderField'] : 'r.tier';
        $orderType = isset($where['orderType']) && strtoupper((string) $where['orderType']) === 'DESC' ? 'desc' : 'asc';

        $query = DiceReward::alias('r')
            ->where('r.direction', $direction)
            ->field('r.id,r.tier,r.direction,r.end_index,r.weight,r.grid_number,r.start_index,r.ui_text,r.real_ev,r.remark,r.type,r.create_time,r.update_time')
            ->order($orderField, $orderType)
            ->order('r.end_index', 'asc');

        if ($tier !== '') {
            $query->where('r.tier', $tier);
        }

        $paginator = $query->paginate($limit, false, ['page' => $page]);
        $arr = $paginator->toArray();
        $data = isset($arr['data']) ? $arr['data'] : $arr['records'] ?? [];
        $total = (int) ($arr['total'] ?? 0);
        $perPage = (int) ($arr['per_page'] ?? $limit);
        $currentPage = (int) ($arr['current_page'] ?? $page);
        foreach ($data as $i => $row) {
            if (isset($row['id']) && $row['id'] !== '' && $row['id'] !== null) {
                $data[$i]['id'] = (int) $row['id'];
            } else {
                $data[$i]['id'] = isset($row['end_index']) ? (int) $row['end_index'] : 0;
            }
            $data[$i]['start_index'] = isset($row['start_index']) && $row['start_index'] !== '' && $row['start_index'] !== null
                ? (int) $row['start_index']
                : 0;
        }
        return [
            'total'         => $total,
            'per_page'      => $perPage,
            'current_page'  => $currentPage,
            'data'          => $data,
        ];
    }

    /**
     * 按单方向批量更新权重（仅更新当前方向的 weight，并刷新缓存）
     * @param int $direction 0=顺时针 1=逆时针
     * @param array<int, array{id: int, weight: int}> $items id 为 end_index（DiceRewardConfig.id）
     */
    public function batchUpdateWeightsByDirection(int $direction, array $items): void
    {
        if (empty($items)) {
            return;
        }
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $id = isset($item['id']) ? (int) $item['id'] : 0;
            $weight = isset($item['weight']) ? (int) $item['weight'] : self::WEIGHT_MIN;
            if ($id <= 0) {
                throw new ApiException('存在无效的配置ID');
            }
            $weight = max(self::WEIGHT_MIN, min(self::WEIGHT_MAX, $weight));

            $tier = DiceRewardConfig::where('id', $id)->value('tier');
            if ($tier === null || $tier === '') {
                throw new ApiException('配置ID ' . $id . ' 不存在或档位为空');
            }
            $tier = (string) $tier;

            $affected = DiceReward::where('tier', $tier)->where('direction', $direction)->where('end_index', $id)->update(['weight' => $weight]);
            if ($affected === 0) {
                $m = new DiceReward();
                $m->tier = $tier;
                $m->direction = $direction;
                $m->end_index = $id;
                $m->weight = $weight;
                $m->save();
            }
        }
        DiceReward::refreshCache();
    }

    /**
     * 按档位+单方向返回列表（用于权重编辑弹窗：当前方向下按档位分组的配置+权重）
     * @param int $direction 0=顺时针 1=逆时针
     * @return array<string, array> 键 T1|T2|...|BIGWIN，值为该档位下带 weight 的行数组
     */
    public function getListGroupedByTierForDirection(int $direction): array
    {
        $configInstance = DiceRewardConfig::getCachedInstance();
        $byTier = $configInstance['by_tier'] ?? [];
        $rewardInstance = DiceReward::getCachedInstance();
        $byTierDirection = $rewardInstance['by_tier_direction'] ?? [];

        $result = [];
        foreach (self::TIER_KEYS as $tier) {
            $result[$tier] = [];
            $rows = $byTier[$tier] ?? [];
            $dirRows = $byTierDirection[$tier][$direction] ?? [];
            $weightMap = [];
            foreach ($dirRows as $r) {
                $eid = isset($r['end_index']) ? (int) $r['end_index'] : 0;
                $weightMap[$eid] = isset($r['weight']) ? max(self::WEIGHT_MIN, min(self::WEIGHT_MAX, (int) $r['weight'])) : 1;
            }
            foreach ($rows as $row) {
                $id = isset($row['id']) ? (int) $row['id'] : 0;
                $result[$tier][] = [
                    'id'          => $id,
                    'grid_number' => $row['grid_number'] ?? 0,
                    'ui_text'      => $row['ui_text'] ?? '',
                    'real_ev'     => $row['real_ev'] ?? 0,
                    'remark'      => $row['remark'] ?? '',
                    'tier'        => $tier,
                    'weight'      => $weightMap[$id] ?? 1,
                ];
            }
        }
        return $result;
    }

    /**
     * 按档位+方向返回 DiceReward 列表（用于权重配比弹窗），直接读 dice_reward 表，不依赖 config
     * 每行含 reward_id(DiceReward 主键，用于按 id 更新权重)、id(end_index 展示用)、grid_number、ui_text、real_ev、remark、weight
     *
     * @return array<string, array{0: array, 1: array}>
     */
    public function getListGroupedByTierWithDirection(): array
    {
        $rewardInstance = DiceReward::getCachedInstance();
        $byTierDirection = $rewardInstance['by_tier_direction'] ?? [];

        $result = [];
        foreach (self::TIER_KEYS as $tier) {
            $result[$tier] = [0 => [], 1 => []];
            foreach ([0, 1] as $direction) {
                $rows = $byTierDirection[$tier][$direction] ?? [];
                foreach ($rows as $r) {
                    $result[$tier][$direction][] = [
                        'reward_id'   => isset($r['id']) ? (int) $r['id'] : 0,
                        'id'          => isset($r['end_index']) ? (int) $r['end_index'] : 0,
                        'grid_number' => isset($r['grid_number']) ? (int) $r['grid_number'] : 0,
                        'ui_text'     => (string) ($r['ui_text'] ?? ''),
                        'real_ev'     => $r['real_ev'] ?? 0,
                        'remark'      => (string) ($r['remark'] ?? ''),
                        'weight'      => isset($r['weight']) ? max(self::WEIGHT_MIN, min(self::WEIGHT_MAX, (int) $r['weight'])) : self::WEIGHT_MIN,
                    ];
                }
            }
        }
        return $result;
    }

    /**
     * 批量更新权重：直接按 DiceReward 主键 id 更新 weight，不依赖 direction/grid_number
     *
     * @param array<int, array{id: int, weight: int}> $items 每项 id 为 dice_reward 表主键，weight 为 1-10000
     * @throws ApiException
     */
    public function batchUpdateWeights(array $items): void
    {
        if (empty($items)) {
            return;
        }
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $id = isset($item['id']) ? (int) $item['id'] : 0;
            $weight = isset($item['weight']) ? (int) $item['weight'] : self::WEIGHT_MIN;
            if ($id <= 0) {
                throw new ApiException('存在无效的 DiceReward id');
            }
            $weight = max(self::WEIGHT_MIN, min(self::WEIGHT_MAX, $weight));
            DiceReward::where('id', $id)->update(['weight' => $weight]);
        }
        DiceReward::refreshCache();
    }

    /** BIGWIN 权重范围：0=0% 中奖，10000=100% 中奖；grid_number=5/30 固定 100% 不可改 */
    private const BIGWIN_WEIGHT_MAX = 10000;

    /**
     * 按 grid_number 获取 BIGWIN 档位权重（取顺时针方向，用于编辑展示）
     * 若 DiceReward 无该点数则 5/30 返回 10000，其余返回 0
     */
    public function getBigwinWeightByGridNumber(int $gridNumber): int
    {
        $inst = DiceReward::getCachedInstance();
        $rows = $inst['by_tier_direction']['BIGWIN'][DiceReward::DIRECTION_CLOCKWISE] ?? [];
        foreach ($rows as $row) {
            if ((int) ($row['grid_number'] ?? 0) === $gridNumber) {
                return min(self::BIGWIN_WEIGHT_MAX, (int) ($row['weight'] ?? self::BIGWIN_WEIGHT_MAX));
            }
        }
        return in_array($gridNumber, [5, 30], true) ? self::BIGWIN_WEIGHT_MAX : 0;
    }

    /**
     * 更新 BIGWIN 档位某点数的权重（顺/逆时针同时更新）；0=0% 中奖，10000=100% 中奖
     * 表 dice_reward 唯一键为 (direction, grid_number)，同一点数同一方向仅一条记录，故先按该键查找再更新，避免重复插入
     */
    public function updateBigwinWeight(int $gridNumber, int $weight): void
    {
        $weight = min(self::BIGWIN_WEIGHT_MAX, max(0, $weight));
        $config = DiceRewardConfig::where('tier', 'BIGWIN')
            ->where('grid_number', $gridNumber)
            ->find();
        if (! $config) {
            return;
        }
        $configArr = $config->toArray();
        foreach ([DiceReward::DIRECTION_CLOCKWISE, DiceReward::DIRECTION_COUNTERCLOCKWISE] as $direction) {
            // 按唯一键 (direction, grid_number) 查找，存在则更新，不存在则插入
            $row = DiceReward::where('direction', $direction)
                ->where('grid_number', $gridNumber)
                ->find();
            if ($row) {
                $row->tier        = 'BIGWIN';
                $row->weight      = $weight > 0 ? $weight : self::WEIGHT_MIN;
                $row->start_index = (int) ($configArr['id'] ?? $row->start_index);
                $row->end_index   = (int) ($configArr['id'] ?? $row->end_index);
                $row->ui_text     = (string) ($configArr['ui_text'] ?? $row->ui_text);
                $row->real_ev     = (float) ($configArr['real_ev'] ?? $row->real_ev);
                $row->remark      = (string) ($configArr['remark'] ?? $row->remark);
                $row->type        = $configArr['type'] ?? $row->type;
                $row->save();
            } else {
                $m = new DiceReward();
                $m->tier        = 'BIGWIN';
                $m->direction   = $direction;
                $m->grid_number = (int) $gridNumber;
                $m->start_index = (int) ($configArr['id'] ?? 0);
                $m->end_index   = (int) ($configArr['id'] ?? 0);
                $m->ui_text     = (string) ($configArr['ui_text'] ?? '');
                $m->real_ev     = (float) ($configArr['real_ev'] ?? 0);
                $m->remark      = (string) ($configArr['remark'] ?? '');
                $m->type        = $configArr['type'] ?? null;
                $m->weight      = $weight > 0 ? $weight : self::WEIGHT_MIN;
                $m->save();
            }
        }
        DiceReward::refreshCache();
    }

    /** 盘面格数（用于顺时针/逆时针计算 end_index） */
    private const BOARD_SIZE = 26;

    /** 点数摇取范围：5-30，顺时针与逆时针均需创建 */
    private const GRID_NUMBER_MIN = 5;
    private const GRID_NUMBER_MAX = 30;

    /**
     * 创建奖励对照：先清空 dice_reward 表，再按两种方向为点数 5-30 生成记录。
     *
     * DiceReward 记录数据规则（与 config 通过 end_index 关联）：
     * - 方向：direction = 0（顺时针）/ 1（逆时针）
     * - 摇取点数：grid_number
     * - 起始索引：start_index = DiceRewardConfig::where('grid_number', $grid_number)->first()->id
     * - 结束索引（顺时针）：end_index = ($start_index + $grid_number) % 26（对 26 取余）
     * - 结束索引（逆时针）：end_index = ($start_index - $grid_number >= 0) ? ($start_index - $grid_number) : (26 + $start_index - $grid_number)
     * - 奖励档位：tier = DiceRewardConfig::where('id', $end_index)->first()->tier
     * - 显示ui：ui_text = DiceRewardConfig::where('id', $end_index)->first()->ui_text
     * - 实际中奖：real_ev = DiceRewardConfig::where('id', $end_index)->first()->real_ev
     * - 备注：remark = DiceRewardConfig::where('id', $end_index)->first()->remark
     * - 类型：type = DiceRewardConfig::where('id', $end_index)->first()->type（-2=唯一惩罚,-1=抽水,0=回本,1=再来一次,2=小赚,3=大奖格）
     * - weight 默认 1，后续在权重编辑弹窗设置
     *
     * 例如顺时针摇取点数为 5 时：start_index = 配置中 grid_number=5 对应格位的 id，
     * 结束位置 = (起始位置 + grid_number) % 26，再取该位置的 config 的 id 作为 end_index。
     * 使用「按 id 排序后的盘面位置 0-25」做环形计算，避免 config.id 非连续时取模结果找不到；
     * 唯一键为 (direction, grid_number)，保证每个点数、每个方向各一条记录，不因 end_index 相同而覆盖。
     *
     * @return array{created_clockwise: int, created_counterclockwise: int, updated_clockwise: int, updated_counterclockwise: int, skipped: int}
     * @throws ApiException
     */
    public function createRewardReferenceFromConfig(): array
    {
        $list = DiceRewardConfig::order('id', 'asc')->select()->toArray();
        if (empty($list)) {
            throw new ApiException('奖励配置为空，请先维护 dice_reward_config');
        }
        $configCount = count($list);
        if ($configCount < self::BOARD_SIZE) {
            throw new ApiException(
                '奖励配置需覆盖 26 个格位（id 0-25 或 1-26），当前仅 ' . $configCount . ' 条，无法完整生成 5-30 共26个点数、顺时针与逆时针的奖励对照'
            );
        }

        $table = (new DiceReward())->getTable();
        Db::execute('DELETE FROM `' . $table . '`');
        DiceReward::refreshCache();

        // 按 id 排序后，盘面位置 0..25 对应 $list[$pos]，避免 config.id 非 0-25/1-26 时取模结果找不到
        $gridToPosition = [];
        foreach ($list as $pos => $row) {
            $gn = isset($row['grid_number']) ? (int) $row['grid_number'] : 0;
            if ($gn >= self::GRID_NUMBER_MIN && $gn <= self::GRID_NUMBER_MAX && !isset($gridToPosition[$gn])) {
                $gridToPosition[$gn] = $pos;
            }
        }

        $createdCw = 0;
        $createdCcw = 0;
        $updatedCw = 0;
        $updatedCcw = 0;
        $skipped = 0;

        for ($gridNumber = self::GRID_NUMBER_MIN; $gridNumber <= self::GRID_NUMBER_MAX; $gridNumber++) {
            if (!isset($gridToPosition[$gridNumber])) {
                $skipped++;
                continue;
            }
            $startPos = $gridToPosition[$gridNumber];
            $startRow = $list[$startPos];
            $startId = isset($startRow['id']) ? (int) $startRow['id'] : 0;

            $endPosCw = ($startPos + $gridNumber) % self::BOARD_SIZE;
            $endPosCcw = $startPos - $gridNumber >= 0 ? $startPos - $gridNumber : self::BOARD_SIZE + $startPos - $gridNumber;

            $configCw = $list[$endPosCw] ?? null;
            $configCcw = $list[$endPosCcw] ?? null;
            $endIdCw = $configCw !== null && isset($configCw['id']) ? (int) $configCw['id'] : 0;
            $endIdCcw = $configCcw !== null && isset($configCcw['id']) ? (int) $configCcw['id'] : 0;

            if ($configCw !== null) {
                $tier = isset($configCw['tier']) ? trim((string) $configCw['tier']) : '';
                if ($tier !== '') {
                    // 使用对应奖励配置的 weight 作为格子权重（若未配置则退回最小权重）
                    $weightCw = isset($configCw['weight']) && $configCw['weight'] !== null
                        ? $configCw['weight']
                        : self::WEIGHT_MIN;
                    $payloadCw = [
                        'tier'        => $tier,
                        'weight'      => $weightCw,
                        'grid_number' => $gridNumber,
                        'start_index' => $startId,
                        'end_index'   => $endIdCw,
                        'ui_text'     => $configCw['ui_text'] ?? '',
                        'real_ev'     => $configCw['real_ev'] ?? null,
                        'remark'      => $configCw['remark'] ?? '',
                        'type'        => isset($configCw['type']) ? (int) $configCw['type'] : 0,
                    ];
                    $existing = DiceReward::where('direction', DiceReward::DIRECTION_CLOCKWISE)->where('grid_number', $gridNumber)->find();
                    if ($existing) {
                        DiceReward::where('id', $existing->id)->update($payloadCw);
                        $updatedCw++;
                    } else {
                        $m = new DiceReward();
                        $m->tier = $tier;
                        $m->direction = DiceReward::DIRECTION_CLOCKWISE;
                        $m->end_index = $endIdCw;
                        $m->weight = $weightCw;
                        $m->grid_number = $gridNumber;
                        $m->start_index = $startId;
                        $m->ui_text = $configCw['ui_text'] ?? '';
                        $m->real_ev = $configCw['real_ev'] ?? null;
                        $m->remark = $configCw['remark'] ?? '';
                        $m->type = isset($configCw['type']) ? (int) $configCw['type'] : 0;
                        $m->save();
                        $createdCw++;
                    }
                }
            }

            if ($configCcw !== null) {
                $tier = isset($configCcw['tier']) ? trim((string) $configCcw['tier']) : '';
                if ($tier !== '') {
                    // 使用对应奖励配置的 weight 作为格子权重（若未配置则退回最小权重）
                    $weightCcw = isset($configCcw['weight']) && $configCcw['weight'] !== null
                        ? $configCcw['weight']
                        : self::WEIGHT_MIN;
                    $payloadCcw = [
                        'tier'        => $tier,
                        'weight'      => $weightCcw,
                        'grid_number' => $gridNumber,
                        'start_index' => $startId,
                        'end_index'   => $endIdCcw,
                        'ui_text'     => $configCcw['ui_text'] ?? '',
                        'real_ev'     => $configCcw['real_ev'] ?? null,
                        'remark'      => $configCcw['remark'] ?? '',
                        'type'        => isset($configCcw['type']) ? (int) $configCcw['type'] : 0,
                    ];
                    $existing = DiceReward::where('direction', DiceReward::DIRECTION_COUNTERCLOCKWISE)->where('grid_number', $gridNumber)->find();
                    if ($existing) {
                        DiceReward::where('id', $existing->id)->update($payloadCcw);
                        $updatedCcw++;
                    } else {
                        $m = new DiceReward();
                        $m->tier = $tier;
                        $m->direction = DiceReward::DIRECTION_COUNTERCLOCKWISE;
                        $m->end_index = $endIdCcw;
                        $m->weight = $weightCcw;
                        $m->grid_number = $gridNumber;
                        $m->start_index = $startId;
                        $m->ui_text = $configCcw['ui_text'] ?? '';
                        $m->real_ev = $configCcw['real_ev'] ?? null;
                        $m->remark = $configCcw['remark'] ?? '';
                        $m->type = isset($configCcw['type']) ? (int) $configCcw['type'] : 0;
                        $m->save();
                        $createdCcw++;
                    }
                }
            }
        }

        DiceReward::refreshCache();
        return [
            'created_clockwise'        => $createdCw,
            'created_counterclockwise'  => $createdCcw,
            'updated_clockwise'         => $updatedCw,
            'updated_counterclockwise' => $updatedCcw,
            'skipped'                  => $skipped,
        ];
    }
}
