<?php

declare(strict_types=1);

namespace App\Framework\Waf;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Timestamp;

/**
 * WAF decision system
 * Final decision-making for request handling based on threat assessment
 */
final readonly class WafDecision
{
    public function __construct(
        public WafAction $action,
        public ThreatAssessment $assessment,
        public string $reason,
        public ?Duration $processingTime = null,
        public ?Timestamp $decisionTime = null,
        public ?int $httpStatusCode = null,
        public ?string $responseMessage = null,
        public array $metadata = []
    ) {
    }

    /**
     * Create decision from threat assessment
     */
    public static function fromAssessment(ThreatAssessment $assessment, Duration $processingTime): self
    {
        $action = self::determineAction($assessment);
        $reason = self::generateReason($assessment, $action);
        $httpStatusCode = self::getHttpStatusCode($action);
        $responseMessage = self::getResponseMessage($action, $assessment);

        return new self(
            action: $action,
            assessment: $assessment,
            reason: $reason,
            processingTime: $processingTime,
            decisionTime: Timestamp::fromFloat(microtime(true)),
            httpStatusCode: $httpStatusCode,
            responseMessage: $responseMessage,
            metadata: self::generateMetadata($assessment)
        );
    }

    /**
     * Create allow decision
     */
    public static function allow(ThreatAssessment $assessment, Duration $processingTime): self
    {
        return new self(
            action: WafAction::ALLOW,
            assessment: $assessment,
            reason: 'Request passed all security checks',
            processingTime: $processingTime,
            decisionTime: Timestamp::fromFloat(microtime(true)),
            httpStatusCode: 200,
            responseMessage: null
        );
    }

    /**
     * Create block decision
     */
    public static function block(ThreatAssessment $assessment, Duration $processingTime, string $reason): self
    {
        return new self(
            action: WafAction::BLOCK,
            assessment: $assessment,
            reason: $reason,
            processingTime: $processingTime,
            decisionTime: Timestamp::fromFloat(microtime(true)),
            httpStatusCode: 403,
            responseMessage: 'Request blocked by security policy'
        );
    }

    /**
     * Create monitor decision (log but allow)
     */
    public static function monitor(ThreatAssessment $assessment, Duration $processingTime, string $reason): self
    {
        return new self(
            action: WafAction::MONITOR,
            assessment: $assessment,
            reason: $reason,
            processingTime: $processingTime,
            decisionTime: Timestamp::fromFloat(microtime(true)),
            httpStatusCode: 200,
            responseMessage: null
        );
    }

    /**
     * Create challenge decision (CAPTCHA/rate limit)
     */
    public static function challenge(ThreatAssessment $assessment, Duration $processingTime, string $reason): self
    {
        return new self(
            action: WafAction::CHALLENGE,
            assessment: $assessment,
            reason: $reason,
            processingTime: $processingTime,
            decisionTime: Timestamp::fromFloat(microtime(true)),
            httpStatusCode: 429,
            responseMessage: 'Request requires verification'
        );
    }

    /**
     * Check if request should be allowed
     */
    public function isAllowed(): bool
    {
        return $this->action === WafAction::ALLOW;
    }

    /**
     * Check if request should be blocked
     */
    public function isBlocked(): bool
    {
        return $this->action === WafAction::BLOCK;
    }

    /**
     * Check if request should be monitored
     */
    public function isMonitored(): bool
    {
        return $this->action === WafAction::MONITOR;
    }

    /**
     * Check if request should be challenged
     */
    public function isChallenged(): bool
    {
        return $this->action === WafAction::CHALLENGE;
    }

    /**
     * Check if request should continue processing
     */
    public function shouldContinue(): bool
    {
        return $this->action === WafAction::ALLOW || $this->action === WafAction::MONITOR;
    }

    /**
     * Check if response should be modified
     */
    public function shouldModifyResponse(): bool
    {
        return $this->action !== WafAction::ALLOW && $this->action !== WafAction::MONITOR;
    }

    /**
     * Get decision priority for logging
     */
    public function getPriority(): string
    {
        return match ($this->action) {
            WafAction::BLOCK => 'HIGH',
            WafAction::CHALLENGE => 'MEDIUM',
            WafAction::MONITOR => 'LOW',
            WafAction::ALLOW => 'INFO'
        };
    }

    /**
     * Get performance metrics
     */
    public function getPerformanceMetrics(): array
    {
        return [
            'processing_time_ms' => $this->processingTime?->toMilliseconds(),
            'detection_count' => $this->assessment->getDetectionCount(),
            'threat_score' => $this->assessment->overallThreatScore->getValue(),
            'decision_time' => $this->decisionTime?->toIsoString(),
        ];
    }

    /**
     * Convert to array for logging/serialization
     */
    public function toArray(): array
    {
        return [
            'action' => $this->action->value,
            'reason' => $this->reason,
            'priority' => $this->getPriority(),
            'http_status_code' => $this->httpStatusCode,
            'response_message' => $this->responseMessage,
            'processing_time_ms' => $this->processingTime?->toMilliseconds(),
            'decision_time' => $this->decisionTime?->toIsoString(),
            'should_continue' => $this->shouldContinue(),
            'should_modify_response' => $this->shouldModifyResponse(),
            'performance_metrics' => $this->getPerformanceMetrics(),
            'metadata' => $this->metadata,
            'assessment' => $this->assessment->toArray(),
        ];
    }

    /**
     * Determine action from threat assessment
     */
    private static function determineAction(ThreatAssessment $assessment): WafAction
    {
        // Empty assessment = allow
        if ($assessment->isEmpty()) {
            return WafAction::ALLOW;
        }

        // Critical threats = block immediately
        if ($assessment->requiresImmediateAction()) {
            return WafAction::BLOCK;
        }

        // Blocking detections = block
        if ($assessment->shouldBlock()) {
            return WafAction::BLOCK;
        }

        // Medium risk with bot indicators = challenge
        $threatScore = $assessment->overallThreatScore->getValue();
        if ($threatScore >= 30.0 && $threatScore < 70.0) {
            // Check for bot-like behavior in threat categories
            $categories = $assessment->getThreatCategories();
            foreach ($categories as $category) {
                if (in_array($category->value, ['bot_detection', 'automation_attack', 'brute_force_attack'])) {
                    return WafAction::CHALLENGE;
                }
            }
        }

        // Low-medium threats = monitor
        if ($assessment->shouldAlert()) {
            return WafAction::MONITOR;
        }

        // No significant threats = allow
        return WafAction::ALLOW;
    }

    /**
     * Generate human-readable reason
     */
    private static function generateReason(ThreatAssessment $assessment, WafAction $action): string
    {
        if ($assessment->isEmpty()) {
            return 'No security threats detected';
        }

        $detectionCount = $assessment->getDetectionCount();
        $threatScore = $assessment->overallThreatScore->getValue();
        $riskLevel = $assessment->getRiskLevel();

        return match ($action) {
            WafAction::BLOCK => sprintf(
                'Request blocked: %d threats detected with %s risk level (score: %.1f)',
                $detectionCount,
                $riskLevel,
                $threatScore
            ),
            WafAction::CHALLENGE => sprintf(
                'Request challenged: %d potential threats detected (score: %.1f)',
                $detectionCount,
                $threatScore
            ),
            WafAction::MONITOR => sprintf(
                'Request monitored: %d suspicious patterns detected (score: %.1f)',
                $detectionCount,
                $threatScore
            ),
            WafAction::ALLOW => sprintf(
                'Request allowed: %d minor issues detected (score: %.1f)',
                $detectionCount,
                $threatScore
            )
        };
    }

    /**
     * Get HTTP status code for action
     */
    private static function getHttpStatusCode(WafAction $action): int
    {
        return match ($action) {
            WafAction::ALLOW, WafAction::MONITOR => 200,
            WafAction::BLOCK => 403,
            WafAction::CHALLENGE => 429
        };
    }

    /**
     * Get response message for action
     */
    private static function getResponseMessage(WafAction $action, ThreatAssessment $assessment): ?string
    {
        return match ($action) {
            WafAction::ALLOW, WafAction::MONITOR => null,
            WafAction::BLOCK => 'Access denied by security policy',
            WafAction::CHALLENGE => 'Please verify you are human to continue'
        };
    }

    /**
     * Generate metadata from assessment
     */
    private static function generateMetadata(ThreatAssessment $assessment): array
    {
        return [
            'threat_categories' => array_map(fn ($cat) => $cat->value, $assessment->getThreatCategories()),
            'owasp_top10_violations' => $assessment->owaspTop10Count,
            'max_severity' => $assessment->maxSeverity->value,
            'average_confidence' => $assessment->averageConfidence->getValue(),
        ];
    }
}
