<?php

namespace app\admin\controller\user;

use app\admin\model\MoneyLogHistory;
use Throwable;
use app\admin\model\User;
use app\admin\model\UserMoneyLog;
use app\common\controller\Backend;
use think\facade\Db;

class MoneyLog extends Backend
{
    /**
     * @var object
     * @phpstan-var UserMoneyLog
     */
    protected object $model;

    protected array $withJoinTable = ['user', 'admin', 'bank'];
    protected array $withTable = ['scoreLog'];

    // 排除字段
    protected string|array $preExcludeFields = ['create_time'];

    protected string|array $quickSearchField = ['user.username', 'user.nickname'];

    public function initialize(): void
    {
        parent::initialize();
        $this->model = new UserMoneyLog();
    }

    /**
     * 验证逻辑
     */
    protected function validateModelData(array $data, string $scene = ''): void
    {
        if (!$this->modelValidate) return;

        $validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
        if (class_exists($validateClass)) {
            $validate = new $validateClass();
            if ($scene) $validate->scene($scene);
            $validate->check($data);
        }
    }

    /**
     * 查看
     * @throws Throwable
     */
    public function index(): void
    {
        if ($this->request->param('select')) {
            $this->select();
        }

        list($where, $alias, $limit, $order) = $this->queryBuilder();
        $res = $this->model
            ->withJoin($this->withJoinTable, $this->withJoinType)
            ->with($this->withTable)
            ->alias($alias)
            ->where($where)
            ->order($order)
            ->paginate($limit);

        $this->success('', [
            'list'   => $res->items(),
            'total'  => $res->total(),
            'remark' => get_route_remark(),
        ]);
    }

    /**
     * 添加
     * @param int $userId
     * @throws Throwable
     */
    public function add(int $userId = 0): void
    {
        $this->error(__('No rows were added'));
        if ($this->request->isPost()) {
            $data = $this->request->post();
            if (!$data) {
                $this->error(__('Parameter %s can not be empty', ['']));
            }
            $result = false;
            $data   = $this->excludeFields($data);

            if ($data['type'] == 2) {
                $data['money'] = -$data['money'];
            }

            $this->model->startTrans();
            try {
                $data['created_by'] = $this->auth->getInfo()['id'];
                $result = $this->model->save($data);
                $this->model->commit();
            } catch (Throwable $e) {
                $this->model->rollback();
                $this->error($e->getMessage());
            }
            if ($result !== false) {
                $this->success(__('Added successfully'));
            } else {
                $this->error(__('No rows were added'));
            }
        }

        $user = User::where('id', $userId)->find();
        if (!$user) {
            $this->error(__("The user can't find it"));
        }
        $this->success('', [
            'user' => $user
        ]);
    }

    /**
     * 编辑
     * @throws Throwable
     */
    public function edit(): void
    {
        $pk  = $this->model->getPk();
        $id  = $this->request->param($pk);
        $row = $this->model->find($id);
        if (!$row) {
            $this->error(__('Record not found'));
        }

        if ($this->request->isPost()) {
            $data = $this->request->post();
            if (!$data) {
                $this->error(__('Parameter %s can not be empty', ['']));
            }

            $result = false;
            $this->model->startTrans();
            try {
                $result = $row->save($data);
                $this->model->commit();
            } catch (Throwable $e) {
                $this->model->rollback();
                $this->error($e->getMessage());
            }
            if ($result !== false) {
                $this->success(__('Update successful'));
            } else {
                $this->error(__('No rows updated'));
            }
            return;
        }
        $this->success('', [
            'row' => $row
        ]);
    }

    public function logHistory($id): void
    {
        $bindFields = [
            'admin' => ['id','username'],
        ];
        $history = MoneyLogHistory::withJoin($bindFields, 'left')
            ->where('money_log_id', $id)
            ->select();
        if (!empty($history)) {
            foreach ($history as &$item) {
                $item['admin_name'] = $item['admin']['username'] ?? '';
                unset($item['admin']);
            }
            unset($item);
        }
        $this->success('', $history);
    }

    public function annualReport()
    {
        $year = $this->request->param('year/d', date('Y'));
        $timeExpression = "create_time";

        $list = Db::table('ba_user_money_log')
            ->whereRaw("FROM_UNIXTIME({$timeExpression}, '%Y') = ?", [$year])
            ->fieldRaw("
            FROM_UNIXTIME({$timeExpression}, '%c') as month,
            
            -- 1. DEPOSIT (分转元，保留2位小数)
            ROUND(SUM(CASE WHEN type IN (1, 3) THEN money / 100 ELSE 0 END), 2) as deposit_amount,
            
            -- 2. WITHDRAW (从变更记录看提现通常存正数，这里直接累加，分转元)
            ROUND(SUM(CASE WHEN type IN (2, 4) THEN money / 100 ELSE 0 END), 2) as withdraw_amount,
            
            -- 3. TRANSACTION (DEPOSIT)
            COUNT(CASE WHEN type IN (1, 3) THEN id END) as deposit_count,
            
            -- 4. TRANSACTION (WITHDRAW)
            COUNT(CASE WHEN type IN (2, 4) THEN id END) as withdraw_count,
            
            -- 5. ACTIVE PLAYER (每月去重活跃用户)
            COUNT(DISTINCT user_id) as active_player,
            
            -- 6. FIRST DEPOSIT (首充去重人数)
            COUNT(DISTINCT CASE WHEN type = 1 AND label = 1 THEN user_id END) as first_deposit_count,
            
            -- 7. UNCLAIM RECEIPT (未领单数)
            COUNT(CASE WHEN type = 1 AND label = 2 THEN id END) as unclaim_count,
            
            -- 8. UNCLAIM AMOUNT (未领金额)
            ROUND(SUM(CASE WHEN type = 1 AND label = 2 THEN money / 100 ELSE 0 END), 2) as unclaim_amount
        ")
            ->group("month")
            ->select()
            ->toArray();

        $monthsData = [];
        foreach ($list as $row) {
            $monthsData[intval($row['month'])] = $row;
        }

        $rowsSkeleton = [
            'deposit'          => ['name' => 'DEPOSIT', 'is_price' => true],
            'withdraw'         => ['name' => 'WITHDRAW', 'is_price' => true],
            'trans_deposit'    => ['name' => 'TRANSACTION (DEPOSIT)', 'is_price' => false],
            'trans_withdraw'   => ['name' => 'TRANSACTION (WITHDRAW)', 'is_price' => false],
            'active_player'    => ['name' => 'ACTIVE PLAYER', 'is_price' => false],
            'first_deposit'    => ['name' => 'FIRST DEPOSIT', 'is_price' => false],
            'unclaim_receipt'  => ['name' => 'UNCLAIM RECEIPT', 'is_price' => false],
            'unclaim_amount'   => ['name' => 'UNCLAIM AMOUNT', 'is_price' => true],
        ];

        $finalReport = [];

        foreach ($rowsSkeleton as $key => $meta) {
            $rowData = [
                'title' => $meta['name']
            ];
            $yearTotal = 0;

            for ($m = 1; $m <= 12; $m++) {
                $monthValue = 0;
                if (isset($monthsData[$m])) {
                    $monthValue = match ($key) {
                        'deposit'         => (float)$monthsData[$m]['deposit_amount'],
                        'withdraw'        => (float)$monthsData[$m]['withdraw_amount'],
                        'trans_deposit'   => (int)$monthsData[$m]['deposit_count'],
                        'trans_withdraw'  => (int)$monthsData[$m]['withdraw_count'],
                        'active_player'   => (int)$monthsData[$m]['active_player'],
                        'first_deposit'   => (int)$monthsData[$m]['first_deposit_count'],
                        'unclaim_receipt' => (int)$monthsData[$m]['unclaim_count'],
                        'unclaim_amount'  => (float)$monthsData[$m]['unclaim_amount'],
                    };
                }

                $rowData['month_' . $m] = $meta['is_price'] ? number_format($monthValue, 2, '.', '') : $monthValue;
                $yearTotal += $monthValue;
            }

            $rowData['year_total'] = $meta['is_price'] ? number_format($yearTotal, 2, '.', '') : $yearTotal;
            $finalReport[] = $rowData;
        }

        $this->success('', [
            'year' => $year,
            'report_table' => $finalReport
        ]);
    }

    public function dailyReport()
    {
        // 1. 从配置中获取动态的小游戏列表
        $games = config('mini_game') ?: [];

        // 2. 接收筛选参数，默认展示当月数据
        $start = $this->request->param('start/s', date('Y-m-01'));
        $end = $this->request->param('end/s', date('Y-m-t'));
        $type = $this->request->param('type/s', 'daily'); // daily, monthly, yearly

        // 转换时间戳范围
        $startTime = strtotime($start . ' 00:00:00');
        $endTime = strtotime($end . ' 23:59:59');

        // 根据 type 动态决定 MySQL 的日期分组格式
        $dateFormat = '%Y-%m-%d';
        if ($type === 'monthly') $dateFormat = '%Y-%m';
        if ($type === 'yearly')  $dateFormat = '%Y';

        // 3. 🌟 动态生成小游戏的 SQL 统计子句
        $promoSqlPieces = [];
        foreach ($games as $gameId => $gameName) {
            // 过滤掉不合法的非数字KEY，防止 SQL 注入风险
            $gameId = (int)$gameId;

            // 顺便给生成的列取个易读的别名，比如：game_268_count, game_268_total
            $promoSqlPieces[] = "COUNT(CASE WHEN game_type = {$gameId} THEN 1 END) AS game_{$gameId}_count";
            $promoSqlPieces[] = "SUM(CASE WHEN game_type = {$gameId} THEN amount ELSE 0 END) AS game_{$gameId}_total";
        }

        // 将动态生成的片段用逗号拼接到一起
        $dynamicPromoSelect = !empty($promoSqlPieces) ? ', ' . implode(', ', $promoSqlPieces) : '';

        // 4. 核心聚合 SQL 1：基于余额变动表统计
        $moneyLogSql = "
        SELECT 
            FROM_UNIXTIME(create_time, '{$dateFormat}') AS report_date,
            COUNT(CASE WHEN type = 1 THEN 1 END) AS deposit_count,
            SUM(CASE WHEN type = 1 THEN money ELSE 0 END) / 100 AS deposit_total,
            COUNT(CASE WHEN type = 2 THEN 1 END) AS withdraw_count,
            SUM(CASE WHEN type = 2 THEN money ELSE 0 END) / 100 AS withdraw_total,
            (SUM(CASE WHEN type = 1 THEN money ELSE 0 END) - SUM(CASE WHEN type = 2 THEN money ELSE 0 END)) / 100 AS win_lose,
            COUNT(CASE WHEN label = 2 THEN 1 END) AS unclaim_count,
            SUM(CASE WHEN label = 2 THEN money ELSE 0 END) / 100 AS unclaim_total,
            COUNT(DISTINCT user_id) AS active_member,
            COUNT(DISTINCT CASE WHEN label = 1 THEN user_id END) AS new_deposit
        FROM ba_user_money_log
        WHERE create_time BETWEEN {$startTime} AND {$endTime}
        GROUP BY report_date
    ";
        $moneyData = Db::query($moneyLogSql);

        // 5. 核心聚合 SQL 2：👉 注入动态字段，统计小游戏奖励
        $promoSql = "
        SELECT 
            FROM_UNIXTIME(create_time, '{$dateFormat}') AS report_date
            {$dynamicPromoSelect} -- 🌟 动态生成的 COUNT 和 SUM 语句放在这里
        FROM ba_promo_reward
        WHERE create_time BETWEEN {$startTime} AND {$endTime}
        GROUP BY report_date
    ";
        $promoData = Db::query($promoSql);

        // 6. 新注册用户统计
        $userRegisterSql = "
        SELECT 
            FROM_UNIXTIME(create_time, '{$dateFormat}') AS report_date,
            COUNT(id) AS new_register
        FROM ba_user
        WHERE create_time BETWEEN {$startTime} AND {$endTime}
        GROUP BY report_date
    ";
        $registerData = class_exists('\think\facade\Db') ? Db::query($userRegisterSql) : [];

        // 7. 数据集结构重组与动态初始化
        $reportMap = [];
        $current = $startTime;
        while ($current <= $endTime) {
            $dateStr = date(str_replace('%', '', $dateFormat), $current);

            // 构建一天的基础数据结构
            $baseRow = [
                'date'           => $dateStr,
                'deposit_count'  => 0,   'deposit_total'   => 0.00,
                'withdraw_count' => 0,   'withdraw_total'  => 0.00,
                'win_lose'       => 0.00,
                'unclaim_count'  => 0,   'unclaim_total'   => 0.00,
                'active_member'  => 0,
                'new_deposit'    => 0,
                'new_register'   => 0,
            ];

            // 🌟 动态初始化游戏字段的初始值（全部设为 0）
            foreach ($games as $gameId => $gameName) {
                $baseRow["game_{$gameId}_count"] = 0;
                $baseRow["game_{$gameId}_total"] = 0.00;
            }

            $reportMap[$dateStr] = $baseRow;
            $current = strtotime("+1 day", $current);
        }

        // 合并资产变动数据
        foreach ($moneyData as $row) {
            if (isset($reportMap[$row['report_date']])) {
                $reportMap[$row['report_date']] = array_merge($reportMap[$row['report_date']], $row);
            }
        }

        // 合并动态小游戏数据
        foreach ($promoData as $row) {
            if (isset($reportMap[$row['report_date']])) {
                $reportMap[$row['report_date']] = array_merge($reportMap[$row['report_date']], $row);
            }
        }

        // 合并注册用户数据
        foreach ($registerData as $row) {
            if (isset($reportMap[$row['report_date']])) {
                $reportMap[$row['report_date']]['new_register'] = $row['new_register'];
            }
        }
        $list = array_values($reportMap);
        // 9. 返回结果给前端组件
        $this->success('Success', [
            'start' => $start,
            'end'   => $end,
            'type'  => $type,
            'games' => $games, // 🌟 把游戏映射送给前端，方便前端循环生成表格的 header 列
            'list'  => $list,
        ]);
    }

    public function customerReport()
    {
        // 1. 获取动态的小游戏配置
        // 示例: [268 => 'plinko ball', 269 => 'smash eggs', 270 => 'spin wheel']
        $games = config('mini_game') ?: [];

        // 2. 接收前端筛选参数
        $start      = $this->request->param('start/s', '');
        $end        = $this->request->param('end/s', '');
        $username   = $this->request->param('username/s', '');
        $loseRebate = $this->request->param('lose_rebate/f', 0); // 输值返利百分比，如输入 5 代表 5%
        $limit      = $this->request->param('limit/d', 10);      // 🌟 每页条数，默认10条

        // 3. 构建用户主表的查询条件
        $userWhere = [];
        if (!empty($username)) {
            $userWhere[] = ['username', 'LIKE', '%' . (string)$username . '%'];
        }

        if (!empty($start) || !empty($end)) {
            $startTime = !empty($start) ? strtotime($start . ' 00:00:00') : 0;
            $endTime   = !empty($end) ? strtotime($end . ' 23:59:59') : time();
            $userWhere[] = ['create_time', 'between', [$startTime, $endTime]];
        }

        // 4. 查询基础用户列表 (对应图片中的基本行)
        // 🌟 假设你的用户主表去前缀叫 'user'，如果不是请更换成实际表名
        $users = Db::name('user')
            ->where($userWhere)
            ->field('id, username, create_time')
            ->order('create_time', 'desc') // 对应图片中最新注册的用户排在最上方
            ->paginate($limit);

        $count       = $users->total();
        $currentPage = $users->currentPage();
        $lastPage    = $users->lastPage();

        // 如果没查到用户，直接返回空列表，免去后续的多表聚合
        if (empty($users->items())) {
            $this->success('Success', [
                'start'        => $start,
                'end'          => $end,
                'username'     => $username,
                'games'        => $games,
                'list'         => [],
                'count'        => $count,
                'current_page' => $currentPage,
                'last_page'    => $lastPage,
            ]);
        }
        // 提取当前页/当前筛选出的所有用户 ID 集合
        $userIds = array_column($users->items(), 'id');
        $userIdsStr = implode(',', $userIds);

        // 5. 🌟 聚合资金流水数据 (Deposit / Withdraw / WinLose)
        $moneyLogSql = "
        SELECT 
            user_id,
            COUNT(CASE WHEN type = 1 THEN 1 END) AS deposit_count,
            SUM(CASE WHEN type = 1 THEN money ELSE 0 END) / 100 AS deposit_total,
            COUNT(CASE WHEN type = 2 THEN 1 END) AS withdraw_count,
            SUM(CASE WHEN type = 2 THEN money ELSE 0 END) / 100 AS withdraw_total,
            (SUM(CASE WHEN type = 1 THEN money ELSE 0 END) - SUM(CASE WHEN type = 2 THEN money ELSE 0 END)) / 100 AS win_lose
        FROM ba_user_money_log
        WHERE user_id IN ({$userIdsStr})
        GROUP BY user_id
    ";
        $moneyData = Db::query($moneyLogSql);
        // 重组为以 user_id 为键的关联数组，方便后续读取
        $moneySummary = array_column($moneyData, null, 'user_id');

        // 6. 🌟 动态拼装并聚合小游戏奖励数据 (与配置中的 gameId 挂钩)
        $promoSummary = [];
        if (!empty($games)) {
            $promoPieces = [];
            foreach ($games as $gameId => $name) {
                $gameId = (int)$gameId;
                $promoPieces[] = "COUNT(CASE WHEN game_type = {$gameId} THEN 1 END) AS game_{$gameId}_count";
                $promoPieces[] = "SUM(CASE WHEN game_type = {$gameId} THEN amount ELSE 0 END) AS game_{$gameId}_total";
            }

            $promoSql = "SELECT user_id, " . implode(', ', $promoPieces) . " 
                     FROM ba_promo_reward 
                     WHERE user_id IN ({$userIdsStr}) 
                     GROUP BY user_id";
            $promoData = Db::query($promoSql);
            $promoSummary = array_column($promoData, null, 'user_id');
        }

        // 7. 🌟 聚合下线推荐人数 (REFERRAL Downline)
        $referralSql = "SELECT parent_id, COUNT(id) AS referral_count 
                    FROM ba_user 
                    WHERE parent_id IN ({$userIdsStr}) 
                    GROUP BY parent_id";
        $referralData = Db::query($referralSql);
        $referralSummary = array_column($referralData, null, 'parent_id');

        // 8. 🌟 循环用户主表，拼装全维度报表行
        $list = [];
        $loseRebatePercent = floatval($loseRebate) / 100; // 转换为小数进行百分比计算

        foreach ($users as $user) {
            $uid = $user['id'];

            // 提取资金数据，若该用户没充提过，则给一套默认清零数据
            $money = $moneySummary[$uid] ?? [
                'deposit_count'  => 0, 'deposit_total'  => 0.00,
                'withdraw_count' => 0, 'withdraw_total' => 0.00,
                'win_lose'       => 0.00
            ];

            // 提取下线推荐人数
            $referralCount = $referralSummary[$uid]['referral_count'] ?? 0;

            // 计算输值返利：只有当 WIN/LOSE 为负数（代表用户输钱）且前端传了返利比时才计算
            $winLose = $money['win_lose'];
            $calculatedRebate = 0.00;
            if ($winLose < 0 && $loseRebatePercent > 0) {
                $calculatedRebate = abs($winLose) * $loseRebatePercent;
            }

            // 组装用户基础维度的列
            $row = [
                'register_date'  => date('Y-m-d H:i:s', $user['create_time']),
                'username'       => $user['username'],
                'deposit_count'  => $money['deposit_count'],
                'deposit_total'  => $money['deposit_total'],
                'withdraw_count' => $money['withdraw_count'],
                'withdraw_total' => $money['withdraw_total'],
                'win_lose'       => $winLose,
                'lose_rebate'    => $calculatedRebate,
                'referral_count' => $referralCount,
            ];

            // 🌟 动态注入配置文件中每一个小游戏的数据列
            foreach ($games as $gameId => $name) {
                $row["game_{$gameId}_count"] = $promoSummary[$uid]["game_{$gameId}_count"] ?? 0;
                $row["game_{$gameId}_total"] = $promoSummary[$uid]["game_{$gameId}_total"] ?? 0.00;
            }

            $list[] = $row;
        }

        // 9. 返回给前端
        $this->success('Success', compact(
            'start', 'end', 'username', 'games', 'list',
            'count', 'currentPage', 'lastPage'
        ));
    }
}