<?php

declare(strict_types=1);

namespace App\Framework\DDoS\Response;

use App\Framework\Cache\Cache;
use App\Framework\CircuitBreaker\CircuitBreaker;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Clock;
use App\Framework\DDoS\DDoSConfig;
use App\Framework\DDoS\Response\Strategies\BlockingStrategy;
use App\Framework\DDoS\Response\Strategies\CaptchaStrategy;
use App\Framework\DDoS\Response\Strategies\RateLimitingStrategy;
use App\Framework\DDoS\Response\Strategies\TarPitStrategy;
use App\Framework\DDoS\Response\ValueObjects\DDoSResponse;
use App\Framework\DDoS\Response\ValueObjects\ResponseAction;
use App\Framework\DDoS\ValueObjects\AttackPattern;
use App\Framework\DDoS\ValueObjects\DDoSAssessment;
use App\Framework\DDoS\ValueObjects\ThreatLevel;
use App\Framework\ErrorHandling\SecurityEventLogger;
use App\Framework\Http\Headers;
use App\Framework\Http\HttpRequest;
use App\Framework\Logging\Logger;
use App\Framework\RateLimit\RateLimiter;

/**
 * Adaptive Response System - Intelligent DDoS mitigation using existing framework modules
 *
 * Coordinates responses through existing security infrastructure:
 * - RateLimiter for traffic shaping
 * - CircuitBreaker for service protection
 * - Cache for response optimization
 * - SecurityEventLogger for SIEM integration
 */
final class AdaptiveResponseSystem
{
    private const string CACHE_PREFIX = 'ddos_response:';

    public function __construct(
        private readonly RateLimiter $rateLimiter,
        private readonly CircuitBreaker $circuitBreaker,
        private readonly Cache $cache,
        private readonly Clock $clock,
        private readonly Logger $logger,
        private readonly SecurityEventLogger $securityLogger,
        private readonly DDoSConfig $config,
        private readonly BlockingStrategy $blockingStrategy,
        private readonly RateLimitingStrategy $rateLimitingStrategy,
        private readonly CaptchaStrategy $captchaStrategy,
        private readonly TarPitStrategy $tarPitStrategy
    ) {
    }

    /**
     * Execute adaptive response based on DDoS assessment
     */
    public function executeResponse(DDoSAssessment $assessment, HttpRequest $request): DDoSResponse
    {
        $responseStart = $this->clock->time();

        // Skip processing for trusted IPs
        if ($this->config->isTrustedIp($assessment->clientIp->value)) {
            return DDoSResponse::allow('trusted_ip');
        }

        // Skip processing for exempt paths
        if ($this->config->isExemptPath($request->path)) {
            return DDoSResponse::allow('exempt_path');
        }

        // Determine response strategy based on threat assessment
        $responseAction = $this->determineResponseAction($assessment);

        // Execute the determined response
        $response = $this->executeResponseAction(
            $responseAction,
            $assessment,
            $request
        );

        // Log response for monitoring
        $this->logResponse($assessment, $responseAction, $response);

        // Update adaptive learning
        $this->updateAdaptiveLearning($assessment, $responseAction, $response);

        $processingTime = $this->clock->time()->diff($responseStart);

        return $response->withProcessingTime($processingTime);
    }

    /**
     * Determine appropriate response action based on assessment
     */
    private function determineResponseAction(DDoSAssessment $assessment): ResponseAction
    {
        // Immediate blocking for critical threats
        if ($assessment->requiresBlocking()) {
            return $this->selectBlockingAction($assessment);
        }

        // Enhanced rate limiting for high threats
        if ($assessment->requiresRateLimiting()) {
            return $this->selectRateLimitingAction($assessment);
        }

        // CAPTCHA challenge for bot-like behavior
        if ($assessment->requiresCaptcha()) {
            return ResponseAction::CAPTCHA_CHALLENGE;
        }

        // Behavioral analysis for medium threats
        if ($assessment->threatLevel === ThreatLevel::MEDIUM) {
            return $this->selectBehavioralAction($assessment);
        }

        // Allow with monitoring for low threats
        return ResponseAction::ALLOW_WITH_MONITORING;
    }

    /**
     * Select appropriate blocking action based on attack patterns
     */
    private function selectBlockingAction(DDoSAssessment $assessment): ResponseAction
    {
        $patterns = $assessment->attackPatterns;

        // Immediate block for volumetric attacks
        if (in_array(AttackPattern::VOLUMETRIC, $patterns)) {
            return ResponseAction::BLOCK_IMMEDIATE;
        }

        // Distributed attacks need coordinated blocking
        if (in_array(AttackPattern::DISTRIBUTED, $patterns)) {
            return ResponseAction::BLOCK_DISTRIBUTED;
        }

        // Botnet attacks get aggressive blocking
        if (in_array(AttackPattern::BOTNET, $patterns)) {
            return ResponseAction::BLOCK_AGGRESSIVE;
        }

        // Application layer attacks get intelligent blocking
        if (in_array(AttackPattern::APPLICATION_LAYER, $patterns)) {
            return ResponseAction::BLOCK_INTELLIGENT;
        }

        // Default to immediate blocking
        return ResponseAction::BLOCK_IMMEDIATE;
    }

    /**
     * Select appropriate rate limiting action
     */
    private function selectRateLimitingAction(DDoSAssessment $assessment): ResponseAction
    {
        $confidence = $assessment->confidence;
        $patterns = $assessment->attackPatterns;

        // Adaptive rate limiting based on confidence
        if ($confidence >= 0.8) {
            return ResponseAction::RATE_LIMIT_AGGRESSIVE;
        }

        if ($confidence >= 0.6) {
            return ResponseAction::RATE_LIMIT_MODERATE;
        }

        // Slowloris attacks need specific handling
        if (in_array(AttackPattern::SLOWLORIS, $patterns)) {
            return ResponseAction::RATE_LIMIT_CONNECTION;
        }

        return ResponseAction::RATE_LIMIT_STANDARD;
    }

    /**
     * Select behavioral response for medium-level threats
     */
    private function selectBehavioralAction(DDoSAssessment $assessment): ResponseAction
    {
        $patterns = $assessment->attackPatterns;

        // Bot behavior gets CAPTCHA
        if (in_array(AttackPattern::BOTNET, $patterns)) {
            return ResponseAction::CAPTCHA_CHALLENGE;
        }

        // Suspicious patterns get tar pit
        if ($assessment->isSuspiciousLocation()) {
            return ResponseAction::TAR_PIT;
        }

        // Default to enhanced monitoring
        return ResponseAction::ENHANCED_MONITORING;
    }

    /**
     * Execute the determined response action
     */
    private function executeResponseAction(
        ResponseAction $action,
        DDoSAssessment $assessment,
        HttpRequest $request
    ): DDoSResponse {
        return match ($action) {
            ResponseAction::BLOCK_IMMEDIATE => $this->blockingStrategy->executeImmediate($assessment, $request),
            ResponseAction::BLOCK_DISTRIBUTED => $this->blockingStrategy->executeDistributed($assessment, $request),
            ResponseAction::BLOCK_AGGRESSIVE => $this->blockingStrategy->executeAggressive($assessment, $request),
            ResponseAction::BLOCK_INTELLIGENT => $this->blockingStrategy->executeIntelligent($assessment, $request),

            ResponseAction::RATE_LIMIT_STANDARD => $this->rateLimitingStrategy->executeStandard($assessment, $request, $this->rateLimiter),
            ResponseAction::RATE_LIMIT_MODERATE => $this->rateLimitingStrategy->executeModerate($assessment, $request, $this->rateLimiter),
            ResponseAction::RATE_LIMIT_AGGRESSIVE => $this->rateLimitingStrategy->executeAggressive($assessment, $request, $this->rateLimiter),
            ResponseAction::RATE_LIMIT_CONNECTION => $this->rateLimitingStrategy->executeConnection($assessment, $request, $this->rateLimiter),

            ResponseAction::CAPTCHA_CHALLENGE => $this->captchaStrategy->executeChallenge($assessment, $request),
            ResponseAction::TAR_PIT => $this->tarPitStrategy->executeTarPit($assessment, $request),
            ResponseAction::ENHANCED_MONITORING => $this->executeEnhancedMonitoring($assessment, $request),
            ResponseAction::ALLOW_WITH_MONITORING => $this->executeAllowWithMonitoring($assessment, $request),

            default => DDoSResponse::allow('unknown_action')
        };
    }

    /**
     * Execute enhanced monitoring response
     */
    private function executeEnhancedMonitoring(DDoSAssessment $assessment, HttpRequest $request): DDoSResponse
    {
        // Enable enhanced logging for this IP
        $monitoringKey = $this->generateKey('monitoring', $assessment->clientIp->value);
        $monitoringData = [
            'start_time' => $this->clock->time()->toFloat(),
            'threat_level' => $assessment->threatLevel->value,
            'confidence' => $assessment->confidence,
            'request_count' => 1,
        ];

        $this->cache->set($monitoringKey, $monitoringData, Duration::fromMinutes(30)->toCacheSeconds());

        // Log security event for SIEM
        $this->securityLogger->logSecurityEvent([
            'event_type' => 'ddos_enhanced_monitoring',
            'client_ip' => $assessment->clientIp->value,
            'threat_level' => $assessment->threatLevel->name,
            'confidence' => $assessment->confidence,
            'request_path' => $request->path,
        ]);

        // Allow request but with tracking headers
        $headers = new Headers([
            'X-DDoS-Protection' => 'monitoring',
            'X-Threat-Level' => $assessment->threatLevel->name,
            'X-Confidence' => (string) round($assessment->confidence * 100),
        ]);

        return DDoSResponse::allow('enhanced_monitoring')->withHeaders($headers);
    }

    /**
     * Execute allow with basic monitoring
     */
    private function executeAllowWithMonitoring(DDoSAssessment $assessment, HttpRequest $request): DDoSResponse
    {
        // Basic request tracking
        $trackingKey = $this->generateKey('tracking', $assessment->clientIp->value);
        $tracking = $this->cache->get($trackingKey, ['count' => 0, 'first_seen' => $this->clock->time()->toFloat()]);
        $tracking['count']++;

        $this->cache->set($trackingKey, $tracking, Duration::fromHours(1)->toCacheSeconds());

        return DDoSResponse::allow('normal_traffic');
    }

    /**
     * Update adaptive learning based on response effectiveness
     */
    private function updateAdaptiveLearning(
        DDoSAssessment $assessment,
        ResponseAction $action,
        DDoSResponse $response
    ): void {
        $learningKey = $this->generateKey('learning', $assessment->clientIp->value);
        $learning = $this->cache->get($learningKey, [
            'action_history' => [],
            'effectiveness' => [],
            'last_updated' => $this->clock->time()->toFloat(),
        ]);

        // Record action taken
        $learning['action_history'][] = [
            'action' => $action->value,
            'threat_level' => $assessment->threatLevel->value,
            'confidence' => $assessment->confidence,
            'timestamp' => $this->clock->time()->toFloat(),
        ];

        // Evaluate effectiveness (simplified)
        $effectiveness = $this->evaluateResponseEffectiveness($assessment, $action, $response);
        $learning['effectiveness'][] = $effectiveness;

        // Keep only recent learning data
        if (count($learning['action_history']) > 100) {
            $learning['action_history'] = array_slice($learning['action_history'], -50);
            $learning['effectiveness'] = array_slice($learning['effectiveness'], -50);
        }

        $learning['last_updated'] = $this->clock->time()->toFloat();

        $this->cache->set($learningKey, $learning, Duration::fromDays(7)->toCacheSeconds());
    }

    /**
     * Evaluate response effectiveness for adaptive learning
     */
    private function evaluateResponseEffectiveness(
        DDoSAssessment $assessment,
        ResponseAction $action,
        DDoSResponse $response
    ): float {
        // Simplified effectiveness calculation
        // In a real implementation, this would track actual attack mitigation

        $baseEffectiveness = match ($action) {
            ResponseAction::BLOCK_IMMEDIATE => 0.9,
            ResponseAction::BLOCK_AGGRESSIVE => 0.85,
            ResponseAction::RATE_LIMIT_AGGRESSIVE => 0.7,
            ResponseAction::CAPTCHA_CHALLENGE => 0.6,
            ResponseAction::TAR_PIT => 0.5,
            default => 0.3
        };

        // Adjust based on threat level alignment
        $threatAlignment = match ([$assessment->threatLevel, $action]) {
            [ThreatLevel::CRITICAL, ResponseAction::BLOCK_IMMEDIATE] => 1.0,
            [ThreatLevel::HIGH, ResponseAction::RATE_LIMIT_AGGRESSIVE] => 1.0,
            [ThreatLevel::MEDIUM, ResponseAction::CAPTCHA_CHALLENGE] => 1.0,
            default => 0.8
        };

        return $baseEffectiveness * $threatAlignment;
    }

    /**
     * Log response for monitoring and analysis
     */
    private function logResponse(
        DDoSAssessment $assessment,
        ResponseAction $action,
        DDoSResponse $response
    ): void {
        if (! $this->config->shouldLogAssessment($assessment->confidence)) {
            return;
        }

        $this->logger->info('DDoS response executed', [
            'client_ip' => $assessment->clientIp->value,
            'threat_level' => $assessment->threatLevel->name,
            'confidence' => $assessment->confidence,
            'action_taken' => $action->value,
            'response_type' => $response->type->value,
            'attack_patterns' => array_map(fn ($p) => $p->value, $assessment->attackPatterns),
            'processing_time_ms' => $assessment->processingTime->toMilliseconds(),
            'timestamp' => $this->clock->time()->format('c'),
        ]);

        // Log security event for high-confidence threats
        if ($assessment->confidence >= 0.7) {
            $this->securityLogger->logSecurityEvent([
                'event_type' => 'ddos_response_executed',
                'severity' => $assessment->threatLevel->name,
                'client_ip' => $assessment->clientIp->value,
                'action' => $action->value,
                'confidence' => $assessment->confidence,
                'attack_patterns' => array_map(fn ($p) => $p->value, $assessment->attackPatterns),
            ]);
        }
    }

    /**
     * Get response statistics for monitoring
     */
    public function getResponseStatistics(?Duration $timeWindow = null): array
    {
        $timeWindow ??= Duration::fromHours(1);
        $statsKey = $this->generateKey('stats', 'global');

        return $this->cache->get($statsKey, [
            'total_responses' => 0,
            'blocked_requests' => 0,
            'rate_limited_requests' => 0,
            'captcha_challenges' => 0,
            'allowed_requests' => 0,
            'average_confidence' => 0.0,
            'top_attack_patterns' => [],
            'response_times' => [],
            'last_updated' => $this->clock->time()->toFloat(),
        ]);
    }

    /**
     * Update response statistics
     */
    public function updateResponseStatistics(
        DDoSAssessment $assessment,
        ResponseAction $action,
        DDoSResponse $response
    ): void {
        $statsKey = $this->generateKey('stats', 'global');
        $stats = $this->getResponseStatistics();

        $stats['total_responses']++;

        // Update action counters
        match ($action) {
            ResponseAction::BLOCK_IMMEDIATE,
            ResponseAction::BLOCK_DISTRIBUTED,
            ResponseAction::BLOCK_AGGRESSIVE,
            ResponseAction::BLOCK_INTELLIGENT => $stats['blocked_requests']++,

            ResponseAction::RATE_LIMIT_STANDARD,
            ResponseAction::RATE_LIMIT_MODERATE,
            ResponseAction::RATE_LIMIT_AGGRESSIVE,
            ResponseAction::RATE_LIMIT_CONNECTION => $stats['rate_limited_requests']++,

            ResponseAction::CAPTCHA_CHALLENGE => $stats['captcha_challenges']++,

            default => $stats['allowed_requests']++
        };

        // Update confidence average
        $stats['average_confidence'] = (
            ($stats['average_confidence'] * ($stats['total_responses'] - 1)) +
            $assessment->confidence
        ) / $stats['total_responses'];

        // Update attack patterns
        foreach ($assessment->attackPatterns as $pattern) {
            $stats['top_attack_patterns'][$pattern->value] =
                ($stats['top_attack_patterns'][$pattern->value] ?? 0) + 1;
        }

        // Update response times
        $stats['response_times'][] = $assessment->processingTime->toMilliseconds();
        if (count($stats['response_times']) > 100) {
            $stats['response_times'] = array_slice($stats['response_times'], -50);
        }

        $stats['last_updated'] = $this->clock->time()->toFloat();

        $this->cache->set($statsKey, $stats, Duration::fromHours(24)->toCacheSeconds());
    }

    /**
     * Check if IP should be automatically unblocked
     */
    public function checkAutoUnblock(string $clientIp): bool
    {
        $blockKey = $this->generateKey('block', $clientIp);
        $blockData = $this->cache->get($blockKey);

        if (! $blockData) {
            return true; // Not blocked
        }

        $blockTime = $blockData['timestamp'] ?? 0;
        $blockDuration = Duration::fromSeconds($blockData['duration'] ?? 300);
        $elapsed = Duration::fromSeconds($this->clock->time()->toFloat() - $blockTime);

        if ($elapsed->greaterThan($blockDuration)) {
            $this->cache->forget($blockKey);

            return true;
        }

        return false;
    }

    private function generateKey(string $type, string $identifier): string
    {
        return self::CACHE_PREFIX . $type . ':' . hash('sha256', $identifier);
    }
}
