<?php

declare(strict_types=1);

namespace App\Framework\Waf\BotProtection\ValueObjects;

use App\Framework\Core\ValueObjects\Timestamp;

/**
 * Browser fingerprint with anomaly detection
 */
final readonly class BrowserFingerprint
{
    public function __construct(
        public string $fingerprintHash,
        public array $features,
        public array $anomalousFeatures,
        public float $anomalyScore,
        public Timestamp $createdAt,
        public ?string $userAgent = null,
        public ?string $acceptLanguage = null,
        public ?string $acceptEncoding = null,
        public ?array $canvasFingerprint = null,
        public ?array $webglFingerprint = null,
        public ?array $audioFingerprint = null,
        public ?array $fontList = null,
        public ?array $screenResolution = null,
        public ?array $timezoneInfo = null,
        public ?array $pluginList = null,
        public bool $touchSupport = false,
        public ?int $colorDepth = null,
        public ?int $pixelRatio = null
    ) {
    }

    /**
     * Create from raw fingerprint data
     */
    public static function fromData(array $data, Timestamp $timestamp): self
    {
        $features = self::extractFeatures($data);
        $anomalousFeatures = self::detectAnomalies($features);
        $anomalyScore = self::calculateAnomalyScore($anomalousFeatures, $features);

        return new self(
            fingerprintHash: self::generateHash($features),
            features: $features,
            anomalousFeatures: $anomalousFeatures,
            anomalyScore: $anomalyScore,
            createdAt: $timestamp,
            userAgent: $data['user_agent'] ?? null,
            acceptLanguage: $data['accept_language'] ?? null,
            acceptEncoding: $data['accept_encoding'] ?? null,
            canvasFingerprint: $data['canvas'] ?? null,
            webglFingerprint: $data['webgl'] ?? null,
            audioFingerprint: $data['audio'] ?? null,
            fontList: $data['fonts'] ?? null,
            screenResolution: $data['screen'] ?? null,
            timezoneInfo: $data['timezone'] ?? null,
            pluginList: $data['plugins'] ?? null,
            touchSupport: $data['touch_support'] ?? false,
            colorDepth: $data['color_depth'] ?? null,
            pixelRatio: $data['pixel_ratio'] ?? null
        );
    }

    /**
     * Check if fingerprint is anomalous
     */
    public function isAnomalous(): bool
    {
        return $this->anomalyScore > 70.0;
    }

    /**
     * Check if fingerprint is highly suspicious
     */
    public function isHighlySuspicious(): bool
    {
        return $this->anomalyScore > 90.0;
    }

    /**
     * Get anomaly score
     */
    public function getAnomalyScore(): float
    {
        return $this->anomalyScore;
    }

    /**
     * Get anomalous features
     */
    public function getAnomalousFeatures(): array
    {
        return $this->anomalousFeatures;
    }

    /**
     * Check if fingerprint matches another
     */
    public function matches(self $other): bool
    {
        return $this->fingerprintHash === $other->fingerprintHash;
    }

    /**
     * Calculate similarity with another fingerprint
     */
    public function similarity(self $other): float
    {
        $commonFeatures = array_intersect_key($this->features, $other->features);
        $totalFeatures = array_merge($this->features, $other->features);

        if (empty($totalFeatures)) {
            return 0.0;
        }

        $matchingFeatures = 0;
        foreach ($commonFeatures as $key => $value) {
            if ($this->features[$key] === $other->features[$key]) {
                $matchingFeatures++;
            }
        }

        return ($matchingFeatures / count($totalFeatures)) * 100;
    }

    /**
     * Extract features from raw data
     */
    private static function extractFeatures(array $data): array
    {
        return [
            'user_agent_hash' => hash('sha256', $data['user_agent'] ?? ''),
            'language' => $data['accept_language'] ?? null,
            'encoding' => $data['accept_encoding'] ?? null,
            'canvas_hash' => isset($data['canvas']) ? hash('sha256', json_encode($data['canvas'])) : null,
            'webgl_hash' => isset($data['webgl']) ? hash('sha256', json_encode($data['webgl'])) : null,
            'audio_hash' => isset($data['audio']) ? hash('sha256', json_encode($data['audio'])) : null,
            'font_count' => isset($data['fonts']) ? count($data['fonts']) : null,
            'screen_signature' => isset($data['screen']) ? implode('x', $data['screen']) : null,
            'timezone_offset' => $data['timezone']['offset'] ?? null,
            'plugin_count' => isset($data['plugins']) ? count($data['plugins']) : null,
            'touch_support' => $data['touch_support'] ?? false,
            'color_depth' => $data['color_depth'] ?? null,
            'pixel_ratio' => $data['pixel_ratio'] ?? null,
        ];
    }

    /**
     * Detect anomalies in features
     */
    private static function detectAnomalies(array $features): array
    {
        $anomalies = [];

        // Check for common bot indicators
        if (isset($features['user_agent_hash']) && self::isSuspiciousUserAgent($features['user_agent_hash'])) {
            $anomalies['suspicious_user_agent'] = 'User agent matches known bot patterns';
        }

        if (isset($features['font_count']) && ($features['font_count'] < 10 || $features['font_count'] > 500)) {
            $anomalies['unusual_font_count'] = "Unusual font count: {$features['font_count']}";
        }

        if (isset($features['plugin_count']) && $features['plugin_count'] === 0) {
            $anomalies['no_plugins'] = 'No browser plugins detected';
        }

        if (! isset($features['canvas_hash']) || ! isset($features['webgl_hash'])) {
            $anomalies['missing_fingerprinting'] = 'Canvas or WebGL fingerprinting blocked';
        }

        if (isset($features['screen_signature'])) {
            $commonResolutions = ['1920x1080', '1366x768', '1440x900', '1280x720'];
            if (! in_array($features['screen_signature'], $commonResolutions)) {
                $anomalies['unusual_resolution'] = "Unusual screen resolution: {$features['screen_signature']}";
            }
        }

        return $anomalies;
    }

    /**
     * Calculate anomaly score
     */
    private static function calculateAnomalyScore(array $anomalies, array $features): float
    {
        $score = 0.0;

        // Base score from anomaly count
        $score += count($anomalies) * 20;

        // Weight specific anomalies
        $weights = [
            'suspicious_user_agent' => 30,
            'unusual_font_count' => 15,
            'no_plugins' => 25,
            'missing_fingerprinting' => 35,
            'unusual_resolution' => 10,
        ];

        foreach ($anomalies as $type => $description) {
            $score += $weights[$type] ?? 10;
        }

        // Cap at 100
        return min(100.0, $score);
    }

    /**
     * Check if user agent is suspicious
     */
    private static function isSuspiciousUserAgent(string $userAgentHash): bool
    {
        // In a real implementation, this would check against a database of known bot user agents
        $knownBotHashes = [
            // Common bot user agent hashes would be stored here
        ];

        return in_array($userAgentHash, $knownBotHashes);
    }

    /**
     * Generate fingerprint hash
     */
    private static function generateHash(array $features): string
    {
        ksort($features);

        return hash('sha256', json_encode($features));
    }

    /**
     * Convert to array
     */
    public function toArray(): array
    {
        return [
            'fingerprint_hash' => $this->fingerprintHash,
            'anomaly_score' => $this->anomalyScore,
            'is_anomalous' => $this->isAnomalous(),
            'is_highly_suspicious' => $this->isHighlySuspicious(),
            'anomalous_features' => $this->anomalousFeatures,
            'created_at' => $this->createdAt->toIsoString(),
            'user_agent' => $this->userAgent,
            'accept_language' => $this->acceptLanguage,
            'canvas_available' => $this->canvasFingerprint !== null,
            'webgl_available' => $this->webglFingerprint !== null,
            'audio_available' => $this->audioFingerprint !== null,
            'font_count' => $this->fontList ? count($this->fontList) : null,
            'screen_resolution' => $this->screenResolution,
            'timezone_info' => $this->timezoneInfo,
            'plugin_count' => $this->pluginList ? count($this->pluginList) : null,
            'touch_support' => $this->touchSupport,
            'color_depth' => $this->colorDepth,
            'pixel_ratio' => $this->pixelRatio,
        ];
    }
}
