<?php

namespace App\Services\Admin;

use App\Models\AdminUser;
use App\Models\AgentNode;
use App\Models\Player;
use App\Support\AdminScopeContext;
use App\Support\AdminScopeContextResolver;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

/** 站点管理员仪表盘：站点规模、今日/近 7 日经营、待结账单摘要。 */
final class SiteDashboardOverviewBuilder
{
    public function __construct(
        private readonly AdminReportQueryService $reportQuery,
    ) {}

    /**
     * @return array<string, mixed>|null
     */
    public function build(AdminUser $admin, AdminScopeContext $scope): ?array
    {
        if (! $admin->hasPermissionCode('dashboard.view')) {
            return null;
        }

        $site = SiteDashboardSiteResolver::resolvePrimarySite($admin);
        if ($site === null) {
            return null;
        }

        $siteId = (int) $site->id;
        $siteCode = (string) $site->code;
        $agentNodeIds = AgentNode::query()
            ->where('admin_site_id', $siteId)
            ->pluck('id')
            ->map(static fn ($id): int => (int) $id)
            ->all();

        $playerCount = (int) Player::query()->where('site_code', $siteCode)->count();
        $today = now()->toDateString();
        $sevenDayFrom = now()->subDays(6)->toDateString();
        $scoped = AdminScopeContextResolver::fromValues(
            $admin,
            requestedSiteCode: $siteCode,
        );
        $todayTotals = $this->reportQuery->periodFinanceTotals($today, $today, $scoped);
        $sevenDayTotals = $this->reportQuery->periodFinanceTotals($sevenDayFrom, $today, $scoped);
        $currencyCode = $this->reportQuery->resolvePeriodCurrencyCode($today, $today, $scoped)
            ?? $this->reportQuery->resolvePeriodCurrencyCode($sevenDayFrom, $today, $scoped);
        $todayActivity = $this->todayActivityStats($siteCode, $today);
        $billStats = SiteSettlementBillStats::forSite($siteId);
        $pendingBills = [
            'count' => $billStats['pending_confirm_count'] + $billStats['payable_count'],
            'unpaid_minor' => $billStats['payable_unpaid_minor'],
        ];
        $topAgentToday = $this->topAgentToday($scoped, $today);

        return [
            'admin_site_id' => $siteId,
            'site_code' => $siteCode,
            'site_name' => (string) $site->name,
            'agent_count' => count($agentNodeIds),
            'player_count' => $playerCount,
            'active_player_count_today' => $todayActivity['player_count'],
            'bet_order_count_today' => $todayActivity['order_count'],
            'today_bet_minor' => $todayTotals['total_bet_minor'],
            'today_payout_minor' => $todayTotals['total_payout_minor'],
            'today_profit_minor' => $todayTotals['approx_house_gross_minor'],
            'seven_day_bet_minor' => $sevenDayTotals['total_bet_minor'],
            'seven_day_payout_minor' => $sevenDayTotals['total_payout_minor'],
            'seven_day_profit_minor' => $sevenDayTotals['approx_house_gross_minor'],
            'profit_scope' => 'house_gross',
            'currency_code' => $currencyCode,
            'pending_bill_count' => $pendingBills['count'],
            'pending_unpaid_minor' => $pendingBills['unpaid_minor'],
            'latest_bet_at' => $todayActivity['latest_bet_at'],
            'top_agent_today' => $topAgentToday,
        ];
    }

    /**
     * @return array{player_count: int, order_count: int, latest_bet_at: ?string}
     */
    private function todayActivityStats(string $siteCode, string $today): array
    {
        $base = DB::table('ticket_orders as o')
            ->join('draws as d', 'd.id', '=', 'o.draw_id')
            ->join('players as p', 'p.id', '=', 'o.player_id')
            ->where('p.site_code', $siteCode)
            ->whereDate('d.business_date', $today);

        $latestBetAt = (clone $base)->max('o.created_at');

        return [
            'player_count' => (int) (clone $base)->distinct('o.player_id')->count('o.player_id'),
            'order_count' => (int) (clone $base)->count(),
            'latest_bet_at' => $latestBetAt !== null ? Carbon::parse((string) $latestBetAt)->toIso8601String() : null,
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    private function topAgentToday(AdminScopeContext $scope, string $today): ?array
    {
        $rows = $this->reportQuery->agentRankingRows($today, $today, null, 1, $scope);

        return $rows[0] ?? null;
    }
}
