<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\SystemClock;
use App\Framework\DDoS\Response\ValueObjects\DDoSResponse;

require_once __DIR__ . '/../../Helpers/TestHelpers.php';

beforeEach(function () {
    $this->clock = new SystemClock();
});

describe('DDoSResponse', function () {

    it('creates allow response correctly', function () {
        $response = DDoSResponse::allow();

        expect($response->action)->toBe('allow');
        expect($response->shouldBlock)->toBeFalse();
        expect($response->httpStatusCode)->toBe(200);
        expect($response->blockDuration)->toBeNull();
    });

    it('creates rate limit response with headers', function () {
        $response = DDoSResponse::rateLimit(
            limit: 60,
            remaining: 45,
            resetTime: $this->clock->time()->add(Duration::fromMinutes(1))
        );

        expect($response->action)->toBe('rate_limit');
        expect($response->shouldBlock)->toBeFalse();
        expect($response->httpStatusCode)->toBe(429);
        expect($response->rateLimitHeaders)->toHaveKeys([
            'X-RateLimit-Limit',
            'X-RateLimit-Remaining',
            'X-RateLimit-Reset',
        ]);
        expect($response->rateLimitHeaders['X-RateLimit-Limit'])->toBe('60');
        expect($response->rateLimitHeaders['X-RateLimit-Remaining'])->toBe('45');
    });

    it('creates block response with duration', function () {
        $blockDuration = Duration::fromMinutes(30);
        $response = DDoSResponse::block(
            duration: $blockDuration,
            reason: 'Malicious activity detected'
        );

        expect($response->action)->toBe('block');
        expect($response->shouldBlock)->toBeTrue();
        expect($response->httpStatusCode)->toBe(403);
        expect($response->blockDuration)->toBe($blockDuration);
        expect($response->blockReason)->toBe('Malicious activity detected');
    });

    it('creates captcha challenge response', function () {
        $challengeData = [
            'captcha_token' => 'abc123',
            'challenge_url' => '/captcha/verify',
            'expires_at' => time() + 300,
        ];

        $response = DDoSResponse::captchaChallenge($challengeData);

        expect($response->action)->toBe('captcha_challenge');
        expect($response->shouldBlock)->toBeFalse();
        expect($response->httpStatusCode)->toBe(202);
        expect($response->challengeData)->toBe($challengeData);
        expect($response->challengeData['captcha_token'])->toBe('abc123');
    });

    it('creates proof of work challenge response', function () {
        $challengeData = [
            'difficulty' => 4,
            'challenge' => 'find_hash_with_4_leading_zeros',
            'algorithm' => 'sha256',
            'expires_at' => time() + 600,
        ];

        $response = DDoSResponse::proofOfWork($challengeData);

        expect($response->action)->toBe('proof_of_work');
        expect($response->shouldBlock)->toBeFalse();
        expect($response->httpStatusCode)->toBe(202);
        expect($response->challengeData)->toBe($challengeData);
        expect($response->challengeData['difficulty'])->toBe(4);
    });

    it('adds custom response headers', function () {
        $response = DDoSResponse::allow()
            ->withHeader('X-DDoS-Protection', 'Active')
            ->withHeader('X-Request-ID', 'req-123');

        expect($response->responseHeaders)->toHaveKeys([
            'X-DDoS-Protection',
            'X-Request-ID',
        ]);
        expect($response->responseHeaders['X-DDoS-Protection'])->toBe('Active');
        expect($response->responseHeaders['X-Request-ID'])->toBe('req-123');
    });

    it('sets security event logging', function () {
        $logContext = [
            'client_ip' => '192.168.1.100',
            'threat_level' => 'HIGH',
            'attack_patterns' => ['VOLUMETRIC_ATTACK'],
        ];

        $response = DDoSResponse::block(Duration::fromMinutes(15), 'High threat detected')
            ->withSecurityEvent($logContext);

        expect($response->securityEventLogged)->toBeTrue();
        expect($response->logContext)->toBe($logContext);
    });

    it('adds response metrics', function () {
        $metrics = [
            'processing_time_ms' => 25,
            'decision_confidence' => 0.8,
            'escalation_level' => 2,
            'historical_offenses' => 3,
        ];

        $response = DDoSResponse::rateLimit(60, 30, $this->clock->time())
            ->withMetrics($metrics);

        expect($response->metrics)->toBe($metrics);
        expect($response->metrics['processing_time_ms'])->toBe(25);
        expect($response->metrics['decision_confidence'])->toBe(0.8);
    });

    it('adds additional protective measures', function () {
        $measures = ['enhanced_logging', 'alert_security_team', 'increase_monitoring'];

        $response = DDoSResponse::block(Duration::fromHours(1), 'Critical threat')
            ->withAdditionalMeasures($measures);

        expect($response->additionalMeasures)->toBe($measures);
        expect($response->additionalMeasures)->toContain('enhanced_logging');
        expect($response->additionalMeasures)->toContain('alert_security_team');
    });

    it('converts to HTTP response array', function () {
        $response = DDoSResponse::rateLimit(60, 45, $this->clock->time())
            ->withHeader('X-Protection', 'Active')
            ->withMetrics(['processing_time_ms' => 30]);

        $httpResponse = $response->toHttpResponse();

        expect($httpResponse)->toHaveKeys([
            'status_code',
            'headers',
            'body',
        ]);

        expect($httpResponse['status_code'])->toBe(429);
        expect($httpResponse['headers'])->toHaveKey('X-RateLimit-Limit');
        expect($httpResponse['headers'])->toHaveKey('X-Protection');
        expect($httpResponse['body'])->toContain('Rate limit exceeded');
    });

    it('exports to array for serialization', function () {
        $response = DDoSResponse::block(Duration::fromMinutes(30), 'Threat detected')
            ->withMetrics(['confidence' => 0.9])
            ->withSecurityEvent(['ip' => '192.168.1.100']);

        $array = $response->toArray();

        expect($array)->toHaveKeys([
            'action',
            'should_block',
            'http_status_code',
            'block_duration_seconds',
            'block_reason',
            'response_headers',
            'metrics',
            'security_event_logged',
            'log_context',
        ]);

        expect($array['action'])->toBe('block');
        expect($array['should_block'])->toBeTrue();
        expect($array['block_duration_seconds'])->toBe(1800); // 30 minutes
    });

    it('creates response from array data', function () {
        $data = [
            'action' => 'rate_limit',
            'should_block' => false,
            'http_status_code' => 429,
            'rate_limit_headers' => [
                'X-RateLimit-Limit' => '60',
                'X-RateLimit-Remaining' => '30',
            ],
            'response_headers' => ['X-Protection' => 'Active'],
            'metrics' => ['processing_time_ms' => 40],
        ];

        $response = DDoSResponse::fromArray($data);

        expect($response->action)->toBe('rate_limit');
        expect($response->shouldBlock)->toBeFalse();
        expect($response->httpStatusCode)->toBe(429);
        expect($response->rateLimitHeaders['X-RateLimit-Limit'])->toBe('60');
        expect($response->responseHeaders['X-Protection'])->toBe('Active');
        expect($response->metrics['processing_time_ms'])->toBe(40);
    });

    it('validates response consistency', function () {
        $blockResponse = DDoSResponse::block(Duration::fromMinutes(15), 'Threat');
        $allowResponse = DDoSResponse::allow();

        expect($blockResponse->shouldBlock)->toBeTrue();
        expect($blockResponse->action)->toBe('block');
        expect($blockResponse->httpStatusCode)->toBe(403);

        expect($allowResponse->shouldBlock)->toBeFalse();
        expect($allowResponse->action)->toBe('allow');
        expect($allowResponse->httpStatusCode)->toBe(200);
    });

    it('handles challenge expiration', function () {
        $challengeData = [
            'captcha_token' => 'abc123',
            'expires_at' => time() - 300, // Expired 5 minutes ago
        ];

        $response = DDoSResponse::captchaChallenge($challengeData);

        expect($response->isChallengeExpired())->toBeTrue();

        $challengeData['expires_at'] = time() + 300; // Expires in 5 minutes
        $response = DDoSResponse::captchaChallenge($challengeData);

        expect($response->isChallengeExpired())->toBeFalse();
    });

    it('calculates response severity', function () {
        $allowResponse = DDoSResponse::allow();
        $rateLimitResponse = DDoSResponse::rateLimit(60, 30, $this->clock->time());
        $blockResponse = DDoSResponse::block(Duration::fromHours(1), 'Critical');

        expect($allowResponse->getSeverity())->toBe('low');
        expect($rateLimitResponse->getSeverity())->toBe('medium');
        expect($blockResponse->getSeverity())->toBe('high');
    });

    it('provides response recommendations', function () {
        $response = DDoSResponse::block(Duration::fromMinutes(30), 'Volumetric attack')
            ->withAdditionalMeasures(['monitor_traffic', 'alert_ops']);

        $recommendations = $response->getRecommendations();

        expect($recommendations)->toBeArray();
        expect($recommendations)->toContain('Review traffic patterns');
        expect($recommendations)->toContain('Consider IP reputation check');
    });

    it('tracks response effectiveness', function () {
        $response = DDoSResponse::rateLimit(60, 30, $this->clock->time());

        // Simulate tracking effectiveness
        $response->recordEffectiveness(0.8, 'Successfully reduced request rate');

        expect($response->getEffectivenessScore())->toBe(0.8);
        expect($response->getEffectivenessNote())->toBe('Successfully reduced request rate');
    });

});
