<?php

declare(strict_types=1);

namespace App\Framework\Waf\BotProtection\ValueObjects;

use App\Framework\Core\ValueObjects\Timestamp;

/**
 * CAPTCHA validation result
 */
final readonly class CaptchaResult
{
    public function __construct(
        public bool $isValid,
        public ?float $score,
        public ?string $action,
        public ?string $hostname,
        public string $challengeId,
        public string $provider,
        public Timestamp $validatedAt,
        public array $errorCodes = [],
        public array $metadata = []
    ) {
    }

    /**
     * Create disabled result
     */
    public static function disabled(): self
    {
        return new self(
            isValid: true, // Allow through when disabled
            score: null,
            action: null,
            hostname: null,
            challengeId: '',
            provider: 'disabled',
            validatedAt: Timestamp::fromFloat(microtime(true)),
            errorCodes: [],
            metadata: ['disabled' => true]
        );
    }

    /**
     * Create invalid result
     */
    public static function invalid(string $reason = 'Invalid response'): self
    {
        return new self(
            isValid: false,
            score: 0.0,
            action: null,
            hostname: null,
            challengeId: '',
            provider: 'unknown',
            validatedAt: Timestamp::fromFloat(microtime(true)),
            errorCodes: ['invalid-response'],
            metadata: ['reason' => $reason]
        );
    }

    /**
     * Check if result indicates human user (high confidence)
     */
    public function isHuman(): bool
    {
        if (! $this->isValid) {
            return false;
        }

        // If score is available, use it
        if ($this->score !== null) {
            return $this->score >= 0.7;
        }

        // For binary CAPTCHA (like hCaptcha), valid = human
        return true;
    }

    /**
     * Check if result indicates likely bot
     */
    public function isBot(): bool
    {
        if (! $this->isValid) {
            return true;
        }

        // If score is available, use it
        if ($this->score !== null) {
            return $this->score < 0.3;
        }

        // For binary CAPTCHA, invalid = bot
        return false;
    }

    /**
     * Check if result is inconclusive
     */
    public function isInconclusive(): bool
    {
        if (! $this->isValid) {
            return false;
        }

        // If score is available, check middle range
        if ($this->score !== null) {
            return $this->score >= 0.3 && $this->score < 0.7;
        }

        // Binary CAPTCHA is never inconclusive
        return false;
    }

    /**
     * Get confidence level
     */
    public function getConfidenceLevel(): string
    {
        if (! $this->isValid) {
            return 'invalid';
        }

        if ($this->score === null) {
            return 'binary'; // Binary CAPTCHA
        }

        return match(true) {
            $this->score >= 0.9 => 'very_high',
            $this->score >= 0.7 => 'high',
            $this->score >= 0.5 => 'medium',
            $this->score >= 0.3 => 'low',
            default => 'very_low'
        };
    }

    /**
     * Check if has errors
     */
    public function hasErrors(): bool
    {
        return ! empty($this->errorCodes);
    }

    /**
     * Get error message
     */
    public function getErrorMessage(): ?string
    {
        if (empty($this->errorCodes)) {
            return null;
        }

        // Map common error codes to user-friendly messages
        $errorMessages = [
            'missing-input-secret' => 'CAPTCHA configuration error',
            'invalid-input-secret' => 'CAPTCHA configuration error',
            'missing-input-response' => 'CAPTCHA response missing',
            'invalid-input-response' => 'Invalid CAPTCHA response',
            'bad-request' => 'CAPTCHA request malformed',
            'timeout-or-duplicate' => 'CAPTCHA expired or already used',
            'score-threshold-not-met' => 'CAPTCHA score too low',
            'hostname-mismatch' => 'CAPTCHA hostname mismatch',
        ];

        $firstError = $this->errorCodes[0];

        return $errorMessages[$firstError] ?? "CAPTCHA error: {$firstError}";
    }

    /**
     * Should request be blocked based on result
     */
    public function shouldBlock(): bool
    {
        return ! $this->isValid || $this->isBot();
    }

    /**
     * Should request be allowed through
     */
    public function shouldAllow(): bool
    {
        return $this->isValid && ($this->isHuman() || $this->provider === 'disabled');
    }

    /**
     * Should request require additional verification
     */
    public function requiresAdditionalVerification(): bool
    {
        return $this->isValid && $this->isInconclusive();
    }

    /**
     * Get response time if available
     */
    public function getResponseTime(): ?float
    {
        return $this->metadata['response_time'] ?? null;
    }

    /**
     * Convert to array
     */
    public function toArray(): array
    {
        return [
            'is_valid' => $this->isValid,
            'score' => $this->score,
            'action' => $this->action,
            'hostname' => $this->hostname,
            'challenge_id' => $this->challengeId,
            'provider' => $this->provider,
            'validated_at' => $this->validatedAt->toIsoString(),
            'error_codes' => $this->errorCodes,
            'metadata' => $this->metadata,
            'is_human' => $this->isHuman(),
            'is_bot' => $this->isBot(),
            'is_inconclusive' => $this->isInconclusive(),
            'confidence_level' => $this->getConfidenceLevel(),
            'has_errors' => $this->hasErrors(),
            'error_message' => $this->getErrorMessage(),
            'should_block' => $this->shouldBlock(),
            'should_allow' => $this->shouldAllow(),
            'requires_additional_verification' => $this->requiresAdditionalVerification(),
            'response_time' => $this->getResponseTime(),
        ];
    }
}
