<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\SystemClock;
use App\Framework\DDoS\ValueObjects\AttackPattern;
use App\Framework\DDoS\ValueObjects\DDoSAssessment;
use App\Framework\DDoS\ValueObjects\ThreatLevel;
use App\Framework\Http\IpAddress;

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

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

describe('DDoSAssessment', function () {

    it('creates valid assessment with all required data', function () {
        $assessment = new DDoSAssessment(
            threatLevel: ThreatLevel::MEDIUM,
            attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: ['traffic_score' => 0.6],
            confidence: 0.8,
            recommendedAction: 'rate_limit',
            processingTime: Duration::fromMilliseconds(50),
            timestamp: $this->clock->time()
        );

        expect($assessment->threatLevel)->toBe(ThreatLevel::MEDIUM);
        expect($assessment->attackPatterns)->toContain(AttackPattern::VOLUMETRIC_ATTACK);
        expect($assessment->clientIp->value)->toBe('192.168.1.100');
        expect($assessment->confidence)->toBe(0.8);
        expect($assessment->recommendedAction)->toBe('rate_limit');
    });

    it('creates safe assessment for normal traffic', function () {
        $assessment = DDoSAssessment::createSafe($this->clock);

        expect($assessment->threatLevel)->toBe(ThreatLevel::LOW);
        expect($assessment->attackPatterns)->toBeEmpty();
        expect($assessment->confidence)->toBeLessThan(0.3);
        expect($assessment->recommendedAction)->toBe('allow');
    });

    it('creates critical assessment for severe threats', function () {
        $assessment = DDoSAssessment::createCritical(
            clientIp: IpAddress::from('10.0.0.1'),
            attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK, AttackPattern::BOT_ATTACK],
            analysisResults: ['threat_score' => 0.95],
            clock: $this->clock
        );

        expect($assessment->threatLevel)->toBe(ThreatLevel::CRITICAL);
        expect($assessment->attackPatterns)->toHaveCount(2);
        expect($assessment->confidence)->toBeGreaterThan(0.9);
        expect($assessment->recommendedAction)->toBe('block');
    });

    it('determines if request should be blocked', function () {
        $lowThreat = new DDoSAssessment(
            threatLevel: ThreatLevel::LOW,
            attackPatterns: [],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [],
            confidence: 0.2,
            recommendedAction: 'allow',
            processingTime: Duration::fromMilliseconds(10),
            timestamp: $this->clock->time()
        );

        $highThreat = new DDoSAssessment(
            threatLevel: ThreatLevel::HIGH,
            attackPatterns: [AttackPattern::APPLICATION_LAYER_ATTACK],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [],
            confidence: 0.8,
            recommendedAction: 'block',
            processingTime: Duration::fromMilliseconds(10),
            timestamp: $this->clock->time()
        );

        expect($lowThreat->shouldBlock())->toBeFalse();
        expect($highThreat->shouldBlock())->toBeTrue();
    });

    it('calculates threat score from analysis results', function () {
        $assessment = new DDoSAssessment(
            threatLevel: ThreatLevel::MEDIUM,
            attackPatterns: [],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [
                'traffic_patterns' => ['threat_score' => 0.6],
                'geo_anomalies' => ['threat_score' => 0.4],
                'waf_analysis' => ['threat_score' => 0.8],
            ],
            confidence: 0.7,
            recommendedAction: 'rate_limit',
            processingTime: Duration::fromMilliseconds(25),
            timestamp: $this->clock->time()
        );

        $threatScore = $assessment->getThreatScore();

        expect($threatScore)->toBeBetween(0.0, 1.0);
        expect($threatScore)->toBeGreaterThan(0.5); // Should be influenced by high scores
    });

    it('provides human readable summary', function () {
        $assessment = new DDoSAssessment(
            threatLevel: ThreatLevel::HIGH,
            attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK, AttackPattern::BOT_ATTACK],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: ['threat_score' => 0.8],
            confidence: 0.9,
            recommendedAction: 'block',
            processingTime: Duration::fromMilliseconds(75),
            timestamp: $this->clock->time()
        );

        $summary = $assessment->getSummary();

        expect($summary)->toBeString();
        expect($summary)->toContain('HIGH');
        expect($summary)->toContain('192.168.1.100');
        expect($summary)->toContain('VOLUMETRIC_ATTACK');
        expect($summary)->toContain('BOT_ATTACK');
    });

    it('exports to array for serialization', function () {
        $assessment = new DDoSAssessment(
            threatLevel: ThreatLevel::MEDIUM,
            attackPatterns: [AttackPattern::DISTRIBUTED_ATTACK],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: ['geo_score' => 0.6],
            confidence: 0.75,
            recommendedAction: 'captcha_challenge',
            processingTime: Duration::fromMilliseconds(30),
            timestamp: $this->clock->time()
        );

        $array = $assessment->toArray();

        expect($array)->toHaveKeys([
            'threat_level',
            'attack_patterns',
            'client_ip',
            'analysis_results',
            'confidence',
            'recommended_action',
            'processing_time_ms',
            'timestamp',
        ]);

        expect($array['threat_level'])->toBe('MEDIUM');
        expect($array['client_ip'])->toBe('192.168.1.100');
        expect($array['confidence'])->toBe(0.75);
    });

    it('creates assessment from array data', function () {
        $data = [
            'threat_level' => 'HIGH',
            'attack_patterns' => ['VOLUMETRIC_ATTACK'],
            'client_ip' => '10.0.0.1',
            'analysis_results' => ['traffic_score' => 0.8],
            'confidence' => 0.85,
            'recommended_action' => 'block',
            'processing_time_ms' => 40,
            'timestamp' => $this->clock->time()->format('c'),
        ];

        $assessment = DDoSAssessment::fromArray($data, $this->clock);

        expect($assessment->threatLevel)->toBe(ThreatLevel::HIGH);
        expect($assessment->attackPatterns)->toContain(AttackPattern::VOLUMETRIC_ATTACK);
        expect($assessment->clientIp->value)->toBe('10.0.0.1');
        expect($assessment->confidence)->toBe(0.85);
    });

    it('validates confidence values', function () {
        expect(fn () => new DDoSAssessment(
            threatLevel: ThreatLevel::LOW,
            attackPatterns: [],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [],
            confidence: 1.5, // Invalid confidence > 1.0
            recommendedAction: 'allow',
            processingTime: Duration::fromMilliseconds(10),
            timestamp: $this->clock->time()
        ))->toThrow(InvalidArgumentException::class);

        expect(fn () => new DDoSAssessment(
            threatLevel: ThreatLevel::LOW,
            attackPatterns: [],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [],
            confidence: -0.1, // Invalid confidence < 0.0
            recommendedAction: 'allow',
            processingTime: Duration::fromMilliseconds(10),
            timestamp: $this->clock->time()
        ))->toThrow(InvalidArgumentException::class);
    });

    it('compares threat levels correctly', function () {
        $lowAssessment = DDoSAssessment::createSafe($this->clock);
        $highAssessment = DDoSAssessment::createCritical(
            clientIp: IpAddress::from('10.0.0.1'),
            attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK],
            analysisResults: [],
            clock: $this->clock
        );

        expect($lowAssessment->isLessThreatThan($highAssessment))->toBeTrue();
        expect($highAssessment->isMoreThreatThan($lowAssessment))->toBeTrue();
        expect($lowAssessment->isMoreThreatThan($highAssessment))->toBeFalse();
    });

    it('identifies coordinated attacks', function () {
        $coordinatedAssessment = new DDoSAssessment(
            threatLevel: ThreatLevel::HIGH,
            attackPatterns: [
                AttackPattern::VOLUMETRIC_ATTACK,
                AttackPattern::DISTRIBUTED_ATTACK,
                AttackPattern::COORDINATED_ATTACK,
            ],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [],
            confidence: 0.9,
            recommendedAction: 'block',
            processingTime: Duration::fromMilliseconds(50),
            timestamp: $this->clock->time()
        );

        expect($coordinatedAssessment->isCoordinatedAttack())->toBeTrue();
        expect($coordinatedAssessment->getAttackComplexity())->toBe('high');
    });

    it('calculates assessment age', function () {
        $pastTime = $this->clock->time()->subtract(Duration::fromMinutes(5));

        $assessment = new DDoSAssessment(
            threatLevel: ThreatLevel::MEDIUM,
            attackPatterns: [],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [],
            confidence: 0.5,
            recommendedAction: 'rate_limit',
            processingTime: Duration::fromMilliseconds(20),
            timestamp: $pastTime
        );

        $age = $assessment->getAge($this->clock->time());

        expect($age->toMinutes())->toBeGreaterThan(4);
        expect($age->toMinutes())->toBeLessThan(6);
    });

    it('determines if assessment is stale', function () {
        $oldTime = $this->clock->time()->subtract(Duration::fromMinutes(10));

        $assessment = new DDoSAssessment(
            threatLevel: ThreatLevel::MEDIUM,
            attackPatterns: [],
            clientIp: IpAddress::from('192.168.1.100'),
            analysisResults: [],
            confidence: 0.5,
            recommendedAction: 'rate_limit',
            processingTime: Duration::fromMilliseconds(20),
            timestamp: $oldTime
        );

        expect($assessment->isStale($this->clock->time(), Duration::fromMinutes(5)))->toBeTrue();
        expect($assessment->isStale($this->clock->time(), Duration::fromMinutes(15)))->toBeFalse();
    });

});
