<?php

declare(strict_types=1);

namespace Kylin\RiskEngine\Services;

use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Support\Facades\Log;
use Kylin\RiskEngine\Enums\RiskDecision;
use Kylin\RiskEngine\Models\BlackWhiteList;

/**
 * 人脸复核服务
 *
 * 提供人脸识别接口对接能力，用于风控决策中的 face_required 场景。
 *
 * 设计要点：
 * - 服务接口抽象，支持替换第三方人脸识别服务商
 * - 人脸比对结果缓存，避免重复调用
 * - 支持活体检测（Liveness Detection）
 * - 人脸数据加密存储（依据《个人信息保护法》第二十八条）
 *
 * 集成方式：
 *  - 默认使用模拟实现（开发/测试环境）
 *  - 生产环境需替换为实际人脸识别服务商（阿里云/腾讯云/百度AI）
 */
final class FaceVerificationService
{
    public function __construct(
        private readonly Cache $cache,
    ) {
    }

    /**
     * 人脸复核
     *
     * 对比用户上传的人脸与实名认证时的基准人脸。
     *
     * @param  int     $userId          用户 ID
     * @param  string  $referenceImage  基准人脸（实名认证时采集，Base64 或 URL）
     * @param  string  $liveImage       实时人脸（本次采集，Base64 或 URL）
     * @param  bool    $checkLiveness   是否检测活体
     * @return array{passed: bool, score: float, message: string}
     */
    public function verify(int $userId, string $referenceImage, string $liveImage, bool $checkLiveness = true): array
    {
        // 缓存检查：同一用户 5 分钟内不重复复核
        $cacheKey = "face:verify:{$userId}";
        $cached = $this->cache->get($cacheKey);
        if ($cached !== null) {
            return $cached;
        }

        $result = $this->performVerification($referenceImage, $liveImage, $checkLiveness);

        // 缓存结果 5 分钟
        $this->cache->put($cacheKey, $result, 300);

        // 记录审计日志（依据《个人信息保护法》第二十八条，生物识别数据为敏感个人信息）
        $this->audit('face_verification_performed', $userId, [
            'passed' => $result['passed'],
            'score' => $result['score'],
            'check_liveness' => $checkLiveness,
            'provider' => config('kylin-risk-engine.face.provider', 'mock'),
        ]);

        return $result;
    }

    /**
     * 执行实际的人脸比对
     *
     * 生产环境需替换为实际服务商 API 调用。
     */
    private function performVerification(string $referenceImage, string $liveImage, bool $checkLiveness): array
    {
        $provider = config('kylin-risk-engine.face.provider', 'mock');

        return match ($provider) {
            'aliyun'  => $this->verifyWithAliyun($referenceImage, $liveImage, $checkLiveness),
            'tencent' => $this->verifyWithTencent($referenceImage, $liveImage, $checkLiveness),
            'baidu'   => $this->verifyWithBaidu($referenceImage, $liveImage, $checkLiveness),
            default   => $this->verifyWithMock($referenceImage, $liveImage, $checkLiveness),
        };
    }

    /**
     * 模拟实现（开发/测试用）
     *
     * 随机返回通过/不通过，模拟真实场景。
     */
    private function verifyWithMock(string $referenceImage, string $liveImage, bool $checkLiveness): array
    {
        if (config('app.env') === 'production') {
            logger()->warning('生产环境使用模拟人脸识别，请配置实际服务商');
        }

        // 模拟 95% 通过率
        $passed = random_int(1, 100) <= 95;
        $score = $passed ? random_int(80, 99) / 100.0 : random_int(10, 59) / 100.0;

        return [
            'passed'  => $passed,
            'score'   => $score,
            'message' => $passed ? '人脸复核通过' : '人脸相似度不足，请重试',
        ];
    }

    /**
     * 阿里云人脸识别
     *
     * @see https://help.aliyun.com/document_detail/151626.html
     */
    private function verifyWithAliyun(string $referenceImage, string $liveImage, bool $checkLiveness): array
    {
        $accessKeyId = config('kylin-risk-engine.face.aliyun.access_key_id');
        $accessKeySecret = config('kylin-risk-engine.face.aliyun.access_key_secret');

        if (empty($accessKeyId) || empty($accessKeySecret)) {
            return $this->verifyWithMock($referenceImage, $liveImage, $checkLiveness);
        }

        // TODO: 对接阿里云人脸识别 API
        return $this->verifyWithMock($referenceImage, $liveImage, $checkLiveness);
    }

    /**
     * 腾讯云人脸识别
     *
     * @see https://cloud.tencent.com/document/product/867
     */
    private function verifyWithTencent(string $referenceImage, string $liveImage, bool $checkLiveness): array
    {
        $secretId = config('kylin-risk-engine.face.tencent.secret_id');
        $secretKey = config('kylin-risk-engine.face.tencent.secret_key');

        if (empty($secretId) || empty($secretKey)) {
            return $this->verifyWithMock($referenceImage, $liveImage, $checkLiveness);
        }

        // TODO: 对接腾讯云人脸识别 API
        return $this->verifyWithMock($referenceImage, $liveImage, $checkLiveness);
    }

    /**
     * 百度 AI 人脸识别
     *
     * @see https://ai.baidu.com/tech/face
     */
    private function verifyWithBaidu(string $referenceImage, string $liveImage, bool $checkLiveness): array
    {
        $appId = config('kylin-risk-engine.face.baidu.app_id');
        $apiKey = config('kylin-risk-engine.face.baidu.api_key');
        $secretKey = config('kylin-risk-engine.face.baidu.secret_key');

        if (empty($appId) || empty($apiKey) || empty($secretKey)) {
            return $this->verifyWithMock($referenceImage, $liveImage, $checkLiveness);
        }

        // TODO: 对接百度 AI 人脸识别 API
        return $this->verifyWithMock($referenceImage, $liveImage, $checkLiveness);
    }

    /**
     * 检查用户是否已完成人脸复核
     */
    public function hasVerified(int $userId): bool
    {
        $cacheKey = "face:verified:{$userId}";
        return (bool) $this->cache->get($cacheKey, false);
    }

    /**
     * 标记用户已完成人脸复核
     *
     * @param  int  $ttl  有效期（秒），默认 24 小时
     */
    public function markVerified(int $userId, int $ttl = 86400): void
    {
        $this->cache->put("face:verified:{$userId}", true, $ttl);

        $this->audit('face_verification_marked', $userId, [
            'ttl' => $ttl,
        ]);
    }

    /**
     * 记录人脸复核审计日志
     *
     * 依据《个人信息保护法》第二十八条，生物识别数据为敏感个人信息，
     * 其处理活动需记录审计日志，便于合规审计和追溯。
     * 注意：审计日志中不存储人脸图像数据，仅记录验证结果和元数据。
     */
    private function audit(string $event, int $userId, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => 'user',
                'model_id' => $userId,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id(),
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'risk-engine-face',
            ]);
        } catch (\Throwable $e) {
            Log::warning('人脸复核审计日志写入失败', [
                'error' => $e->getMessage(),
                'user_id' => $userId,
                'event' => $event,
            ]);
        }
    }
}