<?php

declare(strict_types=1);

namespace app\admin\controller;

use app\common\controller\Backend;
use support\think\Db;
use Webman\Http\Request;
use support\Response;

/**
 * 后台首页统计：按渠道数据范围（非超管仅本渠道）汇总用户、充值、提现、投注等核心指标。
 */
class Dashboard extends Backend
{
    public function index(Request $request): Response
    {
        $response = $this->initializeBackend($request);
        if ($response !== null) {
            return $response;
        }

        $ownerAdminId = $this->ownerAdminIdOrNull();

        $todayStart = strtotime(date('Y-m-d'));
        $todayEnd = $todayStart + 86400 - 1;
        $yesterdayStart = $todayStart - 86400;
        $yesterdayEnd = $todayStart - 1;

        $userTotal = $this->countUsers($ownerAdminId);
        $newToday = $this->countUsersInRange($ownerAdminId, $todayStart, $todayEnd);
        $newYesterday = $this->countUsersInRange($ownerAdminId, $yesterdayStart, $yesterdayEnd);
        $growthPct = null;
        if ($newYesterday > 0) {
            $growthPct = round(($newToday - $newYesterday) / $newYesterday * 100, 1);
        } elseif ($newToday > 0) {
            $growthPct = 100.0;
        }

        $depositAgg = $this->aggregateDepositToday($ownerAdminId, $todayStart, $todayEnd);
        $withdrawPending = $this->countWithdrawPending($ownerAdminId);
        $betAgg = $this->aggregateBetToday($ownerAdminId, $todayStart, $todayEnd);

        $trend = $this->buildSevenDayTrend($ownerAdminId);
        $channelShare = $this->buildChannelShare($ownerAdminId);
        $depositAmountChannelShare = $this->buildDepositAmountChannelShare($ownerAdminId);
        $recentUsers = $this->fetchRecentUsers($ownerAdminId, 10);

        return $this->success('', [
            'remark' => get_route_remark(),
            'stats' => [
                'user_total' => $userTotal,
                'user_new_today' => $newToday,
                'user_new_yesterday' => $newYesterday,
                'user_new_growth_pct' => $growthPct,
                'deposit_today_amount' => $depositAgg['amount'],
                'deposit_today_count' => $depositAgg['count'],
                'withdraw_pending' => $withdrawPending,
                'bet_today_amount' => $betAgg['amount'],
                'bet_today_count' => $betAgg['count'],
            ],
            'trend' => $trend,
            'channel_share' => $channelShare,
            'deposit_amount_channel_share' => $depositAmountChannelShare,
            'recent_users' => $recentUsers,
        ]);
    }

    /**
     * @param int|null $ownerAdminId null=超管不限制；非 null 时 where admin_id
     */
    private function countUsers(?int $ownerAdminId): int
    {
        $q = Db::name('user');
        if ($ownerAdminId !== null) {
            $q->where('admin_id', '=', $ownerAdminId);
        }
        return intval($q->count());
    }

    /**
     * @param int|null $ownerAdminId
     */
    private function countUsersInRange(?int $ownerAdminId, int $start, int $end): int
    {
        $q = Db::name('user')
            ->where('create_time', '>=', $start)
            ->where('create_time', '<=', $end);
        if ($ownerAdminId !== null) {
            $q->where('admin_id', '=', $ownerAdminId);
        }
        return intval($q->count());
    }

    /**
     * 今日成功充值：status=1，按创建日落在今日（与 mock 即时成功一致）。
     *
     * @param int|null $ownerAdminId
     * @return array{count:int, amount:string}
     */
    private function aggregateDepositToday(?int $ownerAdminId, int $todayStart, int $todayEnd): array
    {
        $q = Db::name('deposit_order')
            ->where('status', 1)
            ->where('create_time', '>=', $todayStart)
            ->where('create_time', '<=', $todayEnd);
        if ($ownerAdminId !== null) {
            $q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
        }
        $rows = $q->fieldRaw('COUNT(*) AS c, COALESCE(SUM(CAST(amount AS DECIMAL(18,2))),0) AS s')->find();
        if (!is_array($rows)) {
            $rows = [];
        }
        $count = isset($rows['c']) ? intval($rows['c']) : 0;
        $sum = isset($rows['s']) ? strval($rows['s']) : '0';
        $amount = $this->formatMoney2($sum);

        return ['count' => $count, 'amount' => $amount];
    }

    /**
     * @param int|null $ownerAdminId
     */
    private function countWithdrawPending(?int $ownerAdminId): int
    {
        $q = Db::name('withdraw_order')->where('status', 0);
        if ($ownerAdminId !== null) {
            $q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
        }
        return intval($q->count());
    }

    /**
     * 今日投注：创建时间在今日且订单未作废（status 1 或 2）。
     *
     * @param int|null $ownerAdminId
     * @return array{count:int, amount:string}
     */
    private function aggregateBetToday(?int $ownerAdminId, int $todayStart, int $todayEnd): array
    {
        $q = Db::name('bet_order')
            ->whereIn('status', [1, 2])
            ->where('create_time', '>=', $todayStart)
            ->where('create_time', '<=', $todayEnd);
        if ($ownerAdminId !== null) {
            $q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
        }
        $rows = $q->fieldRaw('COUNT(*) AS c, COALESCE(SUM(CAST(total_amount AS DECIMAL(18,2))),0) AS s')->find();
        if (!is_array($rows)) {
            $rows = [];
        }
        $count = isset($rows['c']) ? intval($rows['c']) : 0;
        $sum = isset($rows['s']) ? strval($rows['s']) : '0';

        return ['count' => $count, 'amount' => $this->formatMoney2($sum)];
    }

    /**
     * @param int|null $ownerAdminId
     * @return array{days:string[], new_users:int[], deposit_amount:string[], bet_amount:string[]}
     */
    private function buildSevenDayTrend(?int $ownerAdminId): array
    {
        $days = [];
        $newUsers = [];
        $depositAmounts = [];
        $betAmounts = [];

        for ($i = 6; $i >= 0; $i--) {
            $dayStart = strtotime(date('Y-m-d', strtotime('-' . $i . ' day')));
            $dayEnd = $dayStart + 86400 - 1;
            $days[] = date('m-d', $dayStart);

            $newUsers[] = $this->countUsersInRange($ownerAdminId, $dayStart, $dayEnd);

            $dq = Db::name('deposit_order')
                ->where('status', 1)
                ->where('create_time', '>=', $dayStart)
                ->where('create_time', '<=', $dayEnd);
            if ($ownerAdminId !== null) {
                $dq->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
            }
            $drow = $dq->fieldRaw('COALESCE(SUM(CAST(amount AS DECIMAL(18,2))),0) AS s')->find();
            $dsum = is_array($drow) && isset($drow['s']) ? strval($drow['s']) : '0';
            $depositAmounts[] = $this->formatMoney2($dsum);

            $bq = Db::name('bet_order')
                ->whereIn('status', [1, 2])
                ->where('create_time', '>=', $dayStart)
                ->where('create_time', '<=', $dayEnd);
            if ($ownerAdminId !== null) {
                $bq->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
            }
            $brow = $bq->fieldRaw('COALESCE(SUM(CAST(total_amount AS DECIMAL(18,2))),0) AS s')->find();
            $bsum = is_array($brow) && isset($brow['s']) ? strval($brow['s']) : '0';
            $betAmounts[] = $this->formatMoney2($bsum);
        }

        return [
            'days' => $days,
            'new_users' => $newUsers,
            'deposit_amount' => $depositAmounts,
            'bet_amount' => $betAmounts,
        ];
    }

    /**
     * 用户按渠道分布（取前 8 名，其余合并为「其他」）。
     *
     * @param int|null $ownerAdminId
     * @return list<array{name:string, value:int}>
     */
    private function buildChannelShare(?int $ownerAdminId): array
    {
        $q = Db::name('user')->fieldRaw('channel_id, COUNT(*) AS c')->group('channel_id');
        if ($ownerAdminId !== null) {
            $q->where('admin_id', '=', $ownerAdminId);
        }
        $rows = $q->orderRaw('c DESC')->select()->toArray();
        if ($rows === []) {
            return [];
        }

        $channelNames = Db::name('channel')->column('name', 'id');

        $list = [];
        foreach ($rows as $row) {
            $cid = $row['channel_id'];
            $cnt = intval($row['c'] ?? 0);
            if ($cid === null || $cid === '' || intval(strval($cid)) === 0) {
                $name = '未分配渠道';
            } else {
                $id = intval(strval($cid));
                $name = $channelNames[$id] ?? ('#' . strval($id));
            }
            $list[] = ['name' => $name, 'value' => $cnt];
        }

        if (count($list) <= 8) {
            return $list;
        }

        $head = array_slice($list, 0, 8);
        $rest = array_slice($list, 8);
        $other = 0;
        foreach ($rest as $item) {
            $other += $item['value'];
        }
        if ($other > 0) {
            $head[] = ['name' => '其他', 'value' => $other];
        }

        return $head;
    }

    /**
     * 成功充值金额按订单归属渠道汇总（status=1，受渠道范围限制）。
     *
     * @param int|null $ownerAdminId
     * @return list<array{name:string, value:string}> value 为两位小数字符串，供前端饼图展示
     */
    private function buildDepositAmountChannelShare(?int $ownerAdminId): array
    {
        $q = Db::name('deposit_order')
            ->where('status', 1)
            ->fieldRaw('channel_id, COALESCE(SUM(CAST(amount AS DECIMAL(18,2))),0) AS s')
            ->group('channel_id');
        if ($ownerAdminId !== null) {
            $q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
        }
        $rows = $q->select()->toArray();
        if ($rows === []) {
            return [];
        }

        $channelNames = Db::name('channel')->column('name', 'id');

        $list = [];
        foreach ($rows as $row) {
            $cid = $row['channel_id'];
            $sumRaw = isset($row['s']) ? strval($row['s']) : '0';
            $amountStr = $this->formatMoney2($sumRaw);
            if (bccomp($amountStr, '0', 2) <= 0) {
                continue;
            }
            if ($cid === null || $cid === '' || intval(strval($cid)) === 0) {
                $name = '未分配渠道';
            } else {
                $id = intval(strval($cid));
                $name = $channelNames[$id] ?? ('#' . strval($id));
            }
            $list[] = [
                'name'  => $name,
                'value' => $amountStr,
            ];
        }

        usort($list, static function (array $a, array $b): int {
            return bccomp($b['value'], $a['value'], 2);
        });

        if (count($list) <= 8) {
            return $list;
        }

        $head = array_slice($list, 0, 8);
        $rest = array_slice($list, 8);
        $other = '0';
        foreach ($rest as $item) {
            $other = bcadd($other, $item['value'], 2);
        }
        $otherFormatted = $this->formatMoney2($other);
        if (bccomp($otherFormatted, '0', 2) > 0) {
            $head[] = [
                'name'  => '其他',
                'value' => $otherFormatted,
            ];
        }

        return $head;
    }

    /**
     * @param int|null $ownerAdminId
     * @return list<array{id:int, username:string, create_time:int, channel_name:string, head_image:string}>
     */
    private function fetchRecentUsers(?int $ownerAdminId, int $limit): array
    {
        $q = Db::name('user')
            ->field(['id', 'username', 'create_time', 'channel_id', 'head_image'])
            ->order('id', 'desc')
            ->limit($limit);
        if ($ownerAdminId !== null) {
            $q->where('admin_id', '=', $ownerAdminId);
        }
        $rows = $q->select()->toArray();
        if ($rows === []) {
            return [];
        }

        $channelNames = Db::name('channel')->column('name', 'id');
        $out = [];
        foreach ($rows as $row) {
            $cid = $row['channel_id'] ?? null;
            if ($cid === null || $cid === '' || intval(strval($cid)) === 0) {
                $cname = '';
            } else {
                $id = intval(strval($cid));
                $cname = $channelNames[$id] ?? '';
            }
            $out[] = [
                'id' => intval($row['id'] ?? 0),
                'username' => strval($row['username'] ?? ''),
                'create_time' => intval($row['create_time'] ?? 0),
                'channel_name' => $cname,
                'head_image' => strval($row['head_image'] ?? ''),
            ];
        }

        return $out;
    }

    private function formatMoney2(string $amount): string
    {
        if (!is_numeric($amount)) {
            return '0.00';
        }
        $normalized = bcadd($amount, '0', 2);

        return bcadd($normalized, '0', 2);
    }

    /**
     * 非超管：按当前管理员名下用户过滤。
     * 超管：返回 null，表示 SQL 不加管理员条件。
     *
     * @return int|null
     */
    private function ownerAdminIdOrNull(): ?int
    {
        if (!$this->auth || $this->auth->isSuperAdmin()) {
            return null;
        }
        $idRaw = $this->auth->id;
        if ($idRaw === null || $idRaw === '' || !is_numeric(strval($idRaw))) {
            return 0;
        }
        $id = intval(strval($idRaw));
        return $id > 0 ? $id : 0;
    }

    /**
     * @return int[]
     */
    private function scopedUserIds(int $ownerAdminId): array
    {
        $ids = Db::name('user')->where('admin_id', '=', $ownerAdminId)->column('id');
        $ids = array_map('intval', $ids);
        return $ids === [] ? [0] : array_values(array_unique($ids));
    }
}
