<?php

namespace Kylin\AntiAddiction\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Kylin\AntiAddiction\Enums\AgeGroup;
use Kylin\AntiAddiction\Models\PlaySession;
use Kylin\AntiAddiction\Models\SpendingRecord;

/**
 * AI 行为分析服务
 *
 * 通过规则引擎检测疑似未成年人行为模式，防止未成年人冒用成年人账号绕过防沉迷。
 *
 * 检测维度：
 * 1. 时段异常：在未成年人禁止时段活跃（成年人账号在凌晨高频游戏）
 * 2. 操作异常：操作频率、游戏习惯与成年人不符
 * 3. 消费异常：消费金额、频率与成年人消费模式不符
 * 4. 社交异常：社交行为模式与成年人不符
 *
 * 综合评分超过阈值时触发人脸识别验证。
 */
class BehaviorAnalysisService
{
    /**
     * 分析用户行为，返回风险评分
     *
     * @param int $userId 用户 ID
     * @return array{risk_score: float, is_anomaly: bool, details: array, should_face_verify: bool}
     */
    public function analyze(int $userId): array
    {
        if (! config('kylin-anti-addiction.behavior_analysis.enabled', true)) {
            return [
                'risk_score' => 0.0,
                'is_anomaly' => false,
                'details' => [],
                'should_face_verify' => false,
            ];
        }

        $rules = config('kylin-anti-addiction.behavior_analysis.rules', []);

        // 各维度评分
        $timeScore = $this->calculateTimeAnomalyScore($userId);
        $operationScore = $this->calculateOperationAnomalyScore($userId);
        $spendingScore = $this->calculateSpendingAnomalyScore($userId);
        $socialScore = $this->calculateSocialAnomalyScore($userId);

        // 加权综合评分
        $riskScore =
            $timeScore * ($rules['abnormal_time_weight'] ?? 0.3) +
            $operationScore * ($rules['abnormal_operation_weight'] ?? 0.25) +
            $spendingScore * ($rules['abnormal_spending_weight'] ?? 0.25) +
            $socialScore * ($rules['abnormal_social_weight'] ?? 0.2);

        $threshold = $rules['threshold'] ?? 0.7;
        $isAnomaly = $riskScore >= $threshold;

        $details = [
            'time_anomaly_score' => round($timeScore, 4),
            'operation_anomaly_score' => round($operationScore, 4),
            'spending_anomaly_score' => round($spendingScore, 4),
            'social_anomaly_score' => round($socialScore, 4),
            'weights' => [
                'time' => $rules['abnormal_time_weight'] ?? 0.3,
                'operation' => $rules['abnormal_operation_weight'] ?? 0.25,
                'spending' => $rules['abnormal_spending_weight'] ?? 0.25,
                'social' => $rules['abnormal_social_weight'] ?? 0.2,
            ],
            'threshold' => $threshold,
        ];

        // 记录分析结果
        if ($isAnomaly) {
            Log::warning('检测到疑似未成年人行为', [
                'user_id' => $userId,
                'risk_score' => $riskScore,
                'details' => $details,
            ]);
        }

        return [
            'risk_score' => round($riskScore, 4),
            'is_anomaly' => $isAnomaly,
            'details' => $details,
            'should_face_verify' => $isAnomaly,
        ];
    }

    // ─── 各维度评分 ─────────────────────────────────────────────

    /**
     * 时段异常评分
     *
     * 检测在未成年人禁止时段（工作日白天、凌晨）的高频游戏行为。
     * 成年人正常行为不会在凌晨 0-6 点高频游戏。
     *
     * 评分规则：
     * - 凌晨 0-6 点游戏次数占比越高，评分越高
     * - 工作日白天 8-16 点游戏占比异常也增加评分
     */
    private function calculateTimeAnomalyScore(int $userId): float
    {
        $cacheKey = "kylin:behavior:time_score:{$userId}";
        $ttl = 3600; // 缓存 1 小时

        return Cache::remember($cacheKey, $ttl, function () use ($userId): float {
            // 获取最近 7 天的会话记录
            $sessions = PlaySession::where('user_id', $userId)
                ->where('session_start', '>=', now()->subDays(7))
                ->get();

            if ($sessions->isEmpty()) {
                return 0.0;
            }

            $totalSessions = $sessions->count();
            $abnormalSessions = 0;

            foreach ($sessions as $session) {
                $hour = (int) $session->session_start->format('H');

                // 凌晨 0-6 点游戏视为异常
                if ($hour >= 0 && $hour < 6) {
                    $abnormalSessions++;
                }
                // 工作日 8-16 点（上学时间）游戏视为异常
                $dayOfWeek = (int) $session->session_start->format('N');
                if ($dayOfWeek >= 1 && $dayOfWeek <= 4 && $hour >= 8 && $hour < 16) {
                    $abnormalSessions++;
                }
            }

            // 异常占比映射到 0-1 评分
            $ratio = $abnormalSessions / $totalSessions;

            return min(1.0, $ratio * 2);
        });
    }

    /**
     * 操作异常评分
     *
     * 检测操作频率和游戏习惯与成年人不符的行为。
     * 未成年人通常游戏频率高、单次时长短、操作密集。
     */
    private function calculateOperationAnomalyScore(int $userId): float
    {
        $cacheKey = "kylin:behavior:operation_score:{$userId}";

        return Cache::remember($cacheKey, 3600, function () use ($userId): float {
            // 获取最近 7 天的会话记录
            $sessions = PlaySession::where('user_id', $userId)
                ->where('session_start', '>=', now()->subDays(7))
                ->whereNotNull('session_end')
                ->get();

            if ($sessions->count() < 3) {
                return 0.0;
            }

            $score = 0.0;

            // 短会话占比（< 30 分钟）—— 未成年人倾向短频快
            $shortSessions = $sessions->filter(fn ($s) => $s->duration < 1800)->count();
            $shortRatio = $shortSessions / $sessions->count();
            if ($shortRatio > 0.7) {
                $score += 0.3;
            }

            // 日均会话数异常（> 5 次/天）
            $dailyAvg = $sessions->count() / 7;
            if ($dailyAvg > 5) {
                $score += 0.3;
            }

            // 会话时长标准差小（游戏时间非常规律，像定时上线）
            $durations = $sessions->pluck('duration')->toArray();
            $mean = array_sum($durations) / count($durations);
            $variance = array_sum(array_map(fn ($d) => ($d - $mean) ** 2, $durations)) / count($durations);
            $stdDev = sqrt($variance);
            if ($stdDev < 300 && $mean > 1800) { // 标准差 < 5 分钟且平均 > 30 分钟
                $score += 0.4;
            }

            return min(1.0, $score);
        });
    }

    /**
     * 消费异常评分
     *
     * 检测消费模式与成年人不符的行为。
     * 未成年人通常小额高频消费，且集中在允许时段。
     */
    private function calculateSpendingAnomalyScore(int $userId): float
    {
        $cacheKey = "kylin:behavior:spending_score:{$userId}";

        return Cache::remember($cacheKey, 3600, function () use ($userId): float {
            $spendings = SpendingRecord::where('user_id', $userId)
                ->where('status', 'success')
                ->where('created_at', '>=', now()->subDays(30))
                ->get();

            if ($spendings->isEmpty()) {
                return 0.0;
            }

            $score = 0.0;

            // 小额消费占比（< 10 元）过高
            $smallSpendings = $spendings->filter(fn ($s) => $s->amount_cents < 1000)->count();
            $smallRatio = $smallSpendings / $spendings->count();
            if ($smallRatio > 0.8) {
                $score += 0.3;
            }

            // 消费时间集中在 20:00-21:00（未成年人允许时段）
            $primeTimeSpendings = $spendings->filter(function ($s) {
                $hour = (int) $s->created_at->format('H');

                return $hour === 20;
            })->count();
            $primeTimeRatio = $primeTimeSpendings / $spendings->count();
            if ($primeTimeRatio > 0.6) {
                $score += 0.4;
            }

            // 消费频率异常（> 3 次/天）
            $dailyAvg = $spendings->count() / 30;
            if ($dailyAvg > 3) {
                $score += 0.3;
            }

            return min(1.0, $score);
        });
    }

    /**
     * 社交异常评分
     *
     * 检测社交行为模式与成年人不符。
     * 此维度为预留扩展，当前返回基础评分。
     */
    private function calculateSocialAnomalyScore(int $userId): float
    {
        // 预留扩展：可对接游戏内社交数据分析
        // 当前返回 0，不参与评分
        return 0.0;
    }
}
