<?php

namespace Kylin\Guardian\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Kylin\Guardian\Enums\GuardianErrorCode;
use Kylin\Guardian\Exceptions\GuardianException;
use Kylin\Guardian\Models\Guardian;
use Kylin\Guardian\Models\GuardianBinding;
use Kylin\User\Models\User;

class GuardianService
{
    /**
     * 注册监护人
     *
     * @param  array{user_id: int, name: string, phone: string, id_card?: string, relationship: string}  $data
     * @return Guardian
     *
     * @throws GuardianException
     */
    public function register(array $data): Guardian
    {
        // 检查是否已注册
        if (Guardian::where('user_id', $data['user_id'])->exists()) {
            throw new GuardianException(GuardianErrorCode::AlreadyRegistered);
        }

        // 验证关系类型
        $this->validateRelationship($data['relationship']);

        return DB::transaction(function () use ($data): Guardian {
            $guardian = Guardian::create([
                'user_id' => $data['user_id'],
                'name' => $data['name'],
                'phone' => $data['phone'],
                'id_card' => $data['id_card'] ?? null,
                'relationship' => $data['relationship'],
                'verified' => ! empty($data['id_card']),
                'verified_at' => ! empty($data['id_card']) ? now() : null,
            ]);

            $this->audit('guardian_registered', $guardian, [
                'user_id' => $guardian->user_id,
                'verified' => $guardian->verified,
            ]);

            return $guardian;
        });
    }

    /**
     * 绑定未成年人
     *
     * @throws GuardianException
     */
    public function bind(int $guardianId, int $minorUserId, string $relationship): GuardianBinding
    {
        $guardian = $this->findGuardian($guardianId);

        // 检查未成年人用户是否存在
        $minorUser = User::find($minorUserId);
        if (! $minorUser) {
            throw new GuardianException(GuardianErrorCode::MinorNotFound);
        }

        // 检查是否为未成年人（根据业务规则判断，此处通过 profile 中的 age 或 birthday 判定）
        if (! $this->isMinor($minorUser)) {
            throw new GuardianException(GuardianErrorCode::NotMinor);
        }

        // 检查是否已存在绑定
        if (GuardianBinding::where('guardian_id', $guardianId)
            ->where('minor_user_id', $minorUserId)
            ->exists()) {
            throw new GuardianException(GuardianErrorCode::BindingAlreadyExists);
        }

        $this->validateRelationship($relationship);

        return DB::transaction(function () use ($guardianId, $minorUserId, $relationship): GuardianBinding {
            $binding = GuardianBinding::create([
                'guardian_id' => $guardianId,
                'minor_user_id' => $minorUserId,
                'relationship' => $relationship,
                'status' => GuardianBinding::STATUS_ACTIVE,
                'play_limit' => config('kylin-guardian.default_play_limit'),
                'spending_limit' => config('kylin-guardian.default_spending_limit'),
                'notify_settings' => config('kylin-guardian.default_notify_settings'),
            ]);

            $this->audit('guardian_bound', $binding->guardian, [
                'minor_user_id' => $minorUserId,
                'relationship' => $relationship,
            ]);

            return $binding;
        });
    }

    /**
     * 解绑
     *
     * @throws GuardianException
     */
    public function unbind(int $bindingId): bool
    {
        $binding = $this->findBinding($bindingId);

        $this->audit('guardian_unbound', $binding->guardian, [
            'minor_user_id' => $binding->minor_user_id,
        ]);

        return $binding->delete();
    }

    /**
     * 获取绑定列表
     *
     * @return \Illuminate\Database\Eloquent\Collection<int, GuardianBinding>
     */
    public function getBindings(int $guardianId)
    {
        return GuardianBinding::where('guardian_id', $guardianId)
            ->with('minorUser')
            ->orderByDesc('created_at')
            ->get();
    }

    /**
     * 获取未成年人游戏报告
     *
     * @return array<string, mixed>
     *
     * @throws GuardianException
     */
    public function getMinorReport(int $bindingId): array
    {
        $binding = $this->findBinding($bindingId);

        $minorUserId = $binding->minor_user_id;
        $today = now()->toDateString();

        // 从缓存或数据源获取游戏时长数据
        $playData = $this->getPlayData($minorUserId, $today);
        $spendingData = $this->getSpendingData($minorUserId, $today);

        return [
            'binding' => [
                'id' => $binding->id,
                'relationship' => $binding->relationship,
                'status' => $binding->status,
            ],
            'minor' => [
                'user_id' => $minorUserId,
                'name' => $binding->minorUser?->name,
            ],
            'today' => [
                'play_minutes' => $playData['total_minutes'],
                'play_limit' => $binding->getDailyPlayMinutes(),
                'remaining_minutes' => max(0, $binding->getDailyPlayMinutes() - $playData['total_minutes']),
                'spending_amount' => $spendingData['total_amount'],
                'spending_limit' => $binding->getDailySpendingMax(),
                'remaining_spending' => max(0, $binding->getDailySpendingMax() - $spendingData['total_amount']),
            ],
            'month' => [
                'spending_amount' => $this->getMonthlySpending($minorUserId),
                'spending_limit' => $binding->getMonthlySpendingMax(),
            ],
            'limits' => [
                'play_limit' => $binding->play_limit,
                'spending_limit' => $binding->spending_limit,
                'allowed_periods' => $binding->getAllowedPeriods(),
            ],
        ];
    }

    /**
     * 设置时间限制
     *
     * @param  array{daily_minutes?: int, allowed_periods?: array, weekend_daily_minutes?: int}  $limits
     *
     * @throws GuardianException
     */
    public function setPlayLimit(int $bindingId, array $limits): GuardianBinding
    {
        $binding = $this->findBinding($bindingId);

        $playLimit = array_merge($binding->play_limit ?? config('kylin-guardian.default_play_limit'), $limits);

        $binding->update(['play_limit' => $playLimit]);

        // 清除远程配置缓存，确保下次获取最新配置
        $this->clearRemoteConfigCache($bindingId);

        return $binding->fresh();
    }

    /**
     * 设置消费限制
     *
     * @param  array{daily_max?: float, monthly_max?: float, single_max?: float}  $limits
     *
     * @throws GuardianException
     */
    public function setSpendingLimit(int $bindingId, array $limits): GuardianBinding
    {
        $binding = $this->findBinding($bindingId);

        $spendingLimit = array_merge(
            $binding->spending_limit ?? config('kylin-guardian.default_spending_limit'),
            $limits
        );

        $binding->update(['spending_limit' => $spendingLimit]);

        // 清除远程配置缓存
        $this->clearRemoteConfigCache($bindingId);

        return $binding->fresh();
    }

    /**
     * 强制下线
     *
     * @throws GuardianException
     */
    public function forceOffline(int $bindingId): bool
    {
        $binding = $this->findBinding($bindingId);

        if (! $binding->isActive()) {
            throw new GuardianException(GuardianErrorCode::BindingDisabled);
        }

        // 将强制下线标记写入缓存，客户端 SDK 轮询检测
        $cacheKey = "guardian:force_offline:{$binding->minor_user_id}";
        Cache::put($cacheKey, true, now()->addHours(24));

        // 更新绑定状态为禁用（阻止再次上线）
        $binding->update(['status' => GuardianBinding::STATUS_DISABLED]);

        $this->audit('guardian_force_offline', $binding->guardian, [
            'minor_user_id' => $binding->minor_user_id,
        ]);

        return true;
    }

    /**
     * 允许上线
     *
     * @throws GuardianException
     */
    public function allowOnline(int $bindingId): bool
    {
        $binding = $this->findBinding($bindingId);

        // 清除强制下线标记
        $cacheKey = "guardian:force_offline:{$binding->minor_user_id}";
        Cache::forget($cacheKey);

        // 恢复绑定状态
        $binding->update(['status' => GuardianBinding::STATUS_ACTIVE]);

        $this->audit('guardian_allow_online', $binding->guardian, [
            'minor_user_id' => $binding->minor_user_id,
        ]);

        return true;
    }

    /**
     * 获取远程配置（供客户端 SDK 拉取）
     *
     * @return array<string, mixed>
     *
     * @throws GuardianException
     */
    public function getRemoteConfig(int $bindingId): array
    {
        $binding = $this->findBinding($bindingId);

        return [
            'binding_id' => $binding->id,
            'status' => $binding->status,
            'force_offline' => Cache::get("guardian:force_offline:{$binding->minor_user_id}", false),
            'play_limit' => [
                'daily_minutes' => $binding->getDailyPlayMinutes(),
                'allowed_periods' => $binding->getAllowedPeriods(),
                'weekend_daily_minutes' => $binding->play_limit['weekend_daily_minutes']
                    ?? config('kylin-guardian.default_play_limit.weekend_daily_minutes', 120),
            ],
            'spending_limit' => [
                'daily_max' => $binding->getDailySpendingMax(),
                'monthly_max' => $binding->getMonthlySpendingMax(),
                'single_max' => $binding->getSingleSpendingMax(),
            ],
            'updated_at' => $binding->updated_at?->toIso8601String(),
        ];
    }

    /**
     * 查找监护人，不存在则抛出异常
     *
     * @throws GuardianException
     */
    public function findGuardian(int $guardianId): Guardian
    {
        $guardian = Guardian::find($guardianId);

        if (! $guardian) {
            throw new GuardianException(GuardianErrorCode::GuardianNotFound);
        }

        return $guardian;
    }

    /**
     * 根据用户ID查找监护人
     *
     * @throws GuardianException
     */
    public function findGuardianByUserId(int $userId): Guardian
    {
        $guardian = Guardian::where('user_id', $userId)->first();

        if (! $guardian) {
            throw new GuardianException(GuardianErrorCode::GuardianNotFound);
        }

        return $guardian;
    }

    /**
     * 查找绑定关系，不存在则抛出异常
     *
     * @throws GuardianException
     */
    public function findBinding(int $bindingId): GuardianBinding
    {
        $binding = GuardianBinding::find($bindingId);

        if (! $binding) {
            throw new GuardianException(GuardianErrorCode::BindingNotFound);
        }

        return $binding;
    }

    /**
     * 验证关系类型是否合法
     *
     * @throws GuardianException
     */
    private function validateRelationship(string $relationship): void
    {
        $validTypes = array_keys(config('kylin-guardian.relationship_types', []));

        if (! in_array($relationship, $validTypes, true)) {
            throw new GuardianException(GuardianErrorCode::InvalidRelationship);
        }
    }

    /**
     * 判断用户是否为未成年人
     */
    private function isMinor(User $user): bool
    {
        // 通过 profile 中的 birthday 判定是否未满 18 周岁
        $profile = $user->profile;

        if (! $profile || ! $profile->birthday) {
            // 无法确定年龄时，默认视为未成年人（保守策略）
            return true;
        }

        return $profile->birthday->addYears(18)->isFuture();
    }

    /**
     * 获取当日游戏时长数据
     *
     * @return array{total_minutes: int, sessions: int}
     */
    private function getPlayData(int $minorUserId, string $date): array
    {
        // 从缓存获取，实际项目中应从游戏时长统计服务获取
        $cacheKey = "guardian:play_data:{$minorUserId}:{$date}";

        return Cache::get($cacheKey, [
            'total_minutes' => 0,
            'sessions' => 0,
        ]);
    }

    /**
     * 获取当日消费数据
     *
     * @return array{total_amount: float, count: int}
     */
    private function getSpendingData(int $minorUserId, string $date): array
    {
        // 从缓存获取，实际项目中应从支付/消费统计服务获取
        $cacheKey = "guardian:spending_data:{$minorUserId}:{$date}";

        return Cache::get($cacheKey, [
            'total_amount' => 0.0,
            'count' => 0,
        ]);
    }

    /**
     * 获取当月消费总额
     */
    private function getMonthlySpending(int $minorUserId): float
    {
        $monthKey = now()->format('Y-m');
        $cacheKey = "guardian:monthly_spending:{$minorUserId}:{$monthKey}";

        return (float) Cache::get($cacheKey, 0.0);
    }

    /**
     * 清除远程配置缓存
     */
    private function clearRemoteConfigCache(int $bindingId): void
    {
        $binding = GuardianBinding::find($bindingId);

        if ($binding) {
            Cache::forget("guardian:remote_config:{$binding->minor_user_id}");
        }
    }

    /**
     * 记录监护人审计日志
     *
     * 依据《未成年人保护法》和《个人信息保护法》，
     * 监护人注册、绑定、解绑、强制下线等操作需记录审计日志。
     */
    private function audit(string $event, Guardian $guardian, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => Guardian::class,
                'model_id' => $guardian->id,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id() ?? $guardian->user_id,
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'guardian',
            ]);
        } catch (\Throwable $e) {
            logger()->warning('监护人审计日志写入失败', [
                'error' => $e->getMessage(),
                'guardian_id' => $guardian->id,
                'event' => $event,
            ]);
        }
    }
}
