<?php

declare(strict_types=1);

/**
 * SQL Injection Specific Testing Module
 * Extends the main tester with SQL injection focused tests
 */

final class SQLInjectionTester extends HackDetectionTester {
    
    /**
     * Run comprehensive SQL injection tests
     */
    public function runSQLInjectionTests(): array {
        echo "Running SQL Injection Specific Tests...\n\n";

        $this->testSQLPatterns();
        $this->testSQLFalsePositives();
        $this->testSQLEvasionTechniques();
        $this->testSQLPerformance();

        return $this->generateSQLTestReport();
    }

    /**
     * Test SQL injection pattern detection
     */
    private function testSQLPatterns(): void {
        echo "1. Testing SQL Injection Patterns...\n";

        $sqlTests = [
            'union_based' => [
                ['input' => "1 UNION SELECT 1,2,3", 'expected_score' => 96],
                ['input' => "1 UNION ALL SELECT username,password FROM users", 'expected_score' => 96],
                ['input' => "1' UNION SELECT 1,@@version,3 --", 'expected_score' => 96],
            ],
            'error_based' => [
                ['input' => "1' AND EXTRACTVALUE(1,CONCAT(0x3a,VERSION())) --", 'expected_score' => 92],
                ['input' => "1' AND UPDATEXML(1,CONCAT(0x3a,(SELECT version())),1) --", 'expected_score' => 92],
            ],
            'boolean_based' => [
                ['input' => "1' OR '1'='1", 'expected_score' => 94],
                ['input' => "admin' --", 'expected_score' => 94],
                ['input' => "1' AND 1=1 --", 'expected_score' => 94],
            ],
            'time_based' => [
                ['input' => "1' AND SLEEP(5) --", 'expected_score' => 93],
                ['input' => "1' AND BENCHMARK(1000000,MD5('test')) --", 'expected_score' => 89],
                ['input' => "1; WAITFOR DELAY '00:00:05' --", 'expected_score' => 91],
            ],
            'stacked_queries' => [
                ['input' => "1; DROP TABLE users --", 'expected_score' => 95],
                ['input' => "1'; INSERT INTO logs VALUES ('attack') --", 'expected_score' => 95],
            ]
        ];

        $this->testResults['sql_patterns'] = [
            'categories_tested' => count($sqlTests),
            'total_tests' => 0,
            'passed_tests' => 0,
            'category_results' => []
        ];

        foreach ($sqlTests as $category => $tests) {
            $categoryResults = [
                'category' => $category,
                'tests' => [],
                'passed' => 0,
                'failed' => 0
            ];

            foreach ($tests as $test) {
                $this->setupTestEnvironment([
                    'get' => ['input' => $test['input']],
                    'server' => [
                        'REQUEST_URI' => '/test?input=' . urlencode($test['input']),
                        'HTTP_USER_AGENT' => 'SQL-Tester/1.0'
                    ]
                ]);

                $result = HackDetectionService::detectHackAttempt();
                $passed = $result['confidence_score'] >= $test['expected_score'];

                $testResult = [
                    'input' => $test['input'],
                    'expected_score' => $test['expected_score'],
                    'actual_score' => $result['confidence_score'],
                    'passed' => $passed,
                    'detected_vectors' => $result['detected_vectors']
                ];

                $categoryResults['tests'][] = $testResult;

                if ($passed) {
                    $categoryResults['passed']++;
                    $this->testResults['sql_patterns']['passed_tests']++;
                } else {
                    $categoryResults['failed']++;
                }

                $this->testResults['sql_patterns']['total_tests']++;
            }

            $this->testResults['sql_patterns']['category_results'][] = $categoryResults;
        }

        echo "   ✓ Tested " . $this->testResults['sql_patterns']['total_tests'] . " SQL injection patterns\n";
    }

    /**
     * Test SQL injection evasion techniques
     */
    private function testSQLEvasionTechniques(): void {
        echo "2. Testing SQL Injection Evasion Techniques...\n";

        $evasionTests = [
            [
                'description' => 'URL encoding evasion',
                'input' => "1 %55%4E%49%4F%4E %53%45%4C%45%43%54 1,2,3",
                'expected_score' => 86
            ],
            [
                'description' => 'Double URL encoding',
                'input' => "1 %2555nion %2553elect 1,2,3",
                'expected_score' => 56
            ],
            [
                'description' => 'Comment obfuscation',
                'input' => "1/*!UNION*/+/*!SELECT*/+1,2,3",
                'expected_score' => 84
            ],
            [
                'description' => 'Mixed case obfuscation',
                'input' => "1 uNiOn sElEcT 1,2,3",
                'expected_score' => 68
            ],
            [
                'description' => 'Whitespace manipulation',
                'input' => "1\u0009UNION\u000BSELECT\u000C1,2,3",
                'expected_score' => 52
            ],
            [
                'description' => 'Hex encoding',
                'input' => "1 UNION SELECT 0x31,0x32,0x33",
                'expected_score' => 86
            ]
        ];

        $this->testResults['sql_evasion'] = [
            'total_tests' => count($evasionTests),
            'passed_tests' => 0,
            'failed_tests' => 0,
            'test_results' => []
        ];

        foreach ($evasionTests as $test) {
            $this->setupTestEnvironment([
                'get' => ['query' => $test['input']],
                'server' => [
                    'REQUEST_URI' => '/search?query=' . urlencode($test['input']),
                    'HTTP_USER_AGENT' => 'Evasion-Tester/1.0'
                ]
            ]);

            $result = HackDetectionService::detectHackAttempt();
            $passed = $result['confidence_score'] >= $test['expected_score'];

            $testResult = [
                'description' => $test['description'],
                'input' => $test['input'],
                'expected_score' => $test['expected_score'],
                'actual_score' => $result['confidence_score'],
                'passed' => $passed,
                'detected_vectors' => $result['detected_vectors']
            ];

            $this->testResults['sql_evasion']['test_results'][] = $testResult;

            if ($passed) {
                $this->testResults['sql_evasion']['passed_tests']++;
            } else {
                $this->testResults['sql_evasion']['failed_tests']++;
            }
        }

        echo "   ✓ Tested " . count($evasionTests) . " evasion techniques\n";
    }

    /**
     * Test SQL injection false positives
     */
    private function testSQLFalsePositives(): void {
        echo "3. Testing SQL Injection False Positives...\n";

        $legitimateInputs = [
            [
                'description' => 'Normal product ID',
                'input' => '12345',
                'should_detect' => false
            ],
            [
                'description' => 'Search with apostrophe',
                'input' => "user's manual",
                'should_detect' => false
            ],
            [
                'description' => 'Email address',
                'input' => "test@example.com",
                'should_detect' => false
            ],
            [
                'description' => 'Complex product name',
                'input' => "Product-Name_v2.0 (Special Edition)",
                'should_detect' => false
            ],
            [
                'description' => 'URL parameters',
                'input' => "category=electronics&sort=price&page=1",
                'should_detect' => false
            ]
        ];

        $this->testResults['sql_false_positives'] = [
            'total_tests' => count($legitimateInputs),
            'passed_tests' => 0,
            'failed_tests' => 0,
            'test_results' => []
        ];

        foreach ($legitimateInputs as $test) {
            $this->setupTestEnvironment([
                'get' => ['q' => $test['input']],
                'server' => [
                    'REQUEST_URI' => '/search?q=' . urlencode($test['input']),
                    'HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                ]
            ]);

            $result = HackDetectionService::detectHackAttempt();
            $passed = $result['is_hack'] === $test['should_detect'];

            $testResult = [
                'description' => $test['description'],
                'input' => $test['input'],
                'expected_detection' => $test['should_detect'],
                'actual_detection' => $result['is_hack'],
                'confidence_score' => $result['confidence_score'],
                'passed' => $passed
            ];

            $this->testResults['sql_false_positives']['test_results'][] = $testResult;

            if ($passed) {
                $this->testResults['sql_false_positives']['passed_tests']++;
            } else {
                $this->testResults['sql_false_positives']['failed_tests']++;
            }
        }

        echo "   ✓ Tested " . count($legitimateInputs) . " legitimate inputs\n";
    }

    /**
     * Test SQL injection detection performance
     */
    private function testSQLPerformance(): void {
        echo "4. Testing SQL Injection Detection Performance...\n";

        $complexQueries = [
            "1' UNION SELECT username,password,email FROM users WHERE admin=1 --",
            "1; DROP TABLE users; CREATE TABLE backdoor (cmd TEXT); --",
            "admin' AND (SELECT COUNT(*) FROM information_schema.tables) > 0 --",
            "1' AND EXTRACTVALUE(1,CONCAT(0x3a,(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables))) --"
        ];

        $performanceTests = [];
        $totalTime = 0;
        $iterations = 100;

        for ($i = 0; $i < $iterations; $i++) {
            $query = $complexQueries[$i % count($complexQueries)];
            
            $this->setupTestEnvironment([
                'get' => ['id' => $query],
                'server' => [
                    'REQUEST_URI' => '/query?id=' . urlencode($query),
                    'HTTP_USER_AGENT' => 'Performance-Tester/1.0'
                ]
            ]);

            $startTime = microtime(true);
            $result = HackDetectionService::detectHackAttempt();
            $endTime = microtime(true);

            $executionTime = $endTime - $startTime;
            $totalTime += $executionTime;

            $performanceTests[] = [
                'iteration' => $i + 1,
                'query' => substr($query, 0, 50) . '...',
                'execution_time' => $executionTime,
                'detected' => $result['is_hack'],
                'score' => $result['confidence_score']
            ];
        }

        $this->testResults['sql_performance'] = [
            'iterations' => $iterations,
            'average_time' => $totalTime / $iterations,
            'total_time' => $totalTime,
            'detection_rate' => count(array_filter($performanceTests, fn($test) => $test['detected'])) / $iterations * 100,
            'tests' => $performanceTests
        ];

        echo "   ✓ Completed " . $iterations . " performance iterations\n";
    }

    /**
     * Generate SQL-specific test report
     */
    private function generateSQLTestReport(): array {
        $report = [
            'sql_test_summary' => [
                'total_pattern_tests' => $this->testResults['sql_patterns']['total_tests'] ?? 0,
                'pattern_success_rate' => $this->testResults['sql_patterns']['total_tests'] > 0 ? 
                    ($this->testResults['sql_patterns']['passed_tests'] / $this->testResults['sql_patterns']['total_tests']) * 100 : 0,
                'evasion_detection_rate' => $this->testResults['sql_evasion']['passed_tests'] / $this->testResults['sql_evasion']['total_tests'] * 100,
                'false_positive_rate' => $this->testResults['sql_false_positives']['passed_tests'] / $this->testResults['sql_false_positives']['total_tests'] * 100,
                'average_detection_time' => $this->testResults['sql_performance']['average_time'] ?? 0
            ],
            'detailed_sql_results' => $this->testResults
        ];

        $this->printSQLReport($report);

        return $report;
    }

    /**
     * Print SQL-specific test report
     */
    private function printSQLReport(array $report): void {
        echo "\n" . str_repeat("=", 70) . "\n";
        echo "SQL INJECTION DETECTION TEST REPORT\n";
        echo str_repeat("=", 70) . "\n";

        $summary = $report['sql_test_summary'];
        
        printf("Pattern Detection Success: %.1f%%\n", $summary['pattern_success_rate']);
        printf("Evasion Technique Detection: %.1f%%\n", $summary['evasion_detection_rate']);
        printf("False Positive Prevention: %.1f%%\n", $summary['false_positive_rate']);
        printf("Average Detection Time: %.4f seconds\n", $summary['average_detection_time']);
        printf("Total Pattern Tests: %d\n", $summary['total_pattern_tests']);

        echo "\nCATEGORY BREAKDOWN:\n";
        echo str_repeat("-", 50) . "\n";

        $categories = [
            'Union-based' => 'union_based',
            'Error-based' => 'error_based', 
            'Boolean-based' => 'boolean_based',
            'Time-based' => 'time_based',
            'Stacked Queries' => 'stacked_queries'
        ];

        foreach ($categories as $name => $key) {
            foreach ($this->testResults['sql_patterns']['category_results'] as $category) {
                if ($category['category'] === $key) {
                    $successRate = $category['tests'] > 0 ? ($category['passed'] / count($category['tests'])) * 100 : 0;
                    printf("%-15s: %.1f%% (%d/%d)\n", $name, $successRate, $category['passed'], count($category['tests']));
                    break;
                }
            }
        }

        echo str_repeat("=", 70) . "\n";

        // Recommendations
        if ($summary['pattern_success_rate'] < 95) {
            echo "RECOMMENDATIONS:\n";
            echo "- Review pattern matching for failed test cases\n";
            echo "- Adjust detection thresholds if necessary\n";
            echo "- Add additional patterns for missed attack vectors\n";
        } else {
            echo "✓ SQL injection detection is performing excellently.\n";
            echo "✓ All critical attack vectors are properly detected.\n";
            echo "✓ False positive rate is within acceptable limits.\n";
        }
    }
}

// Command line interface for SQL injection testing
if (php_sapi_name() === 'cli' && basename(__FILE__) === basename($_SERVER['argv'][0] ?? '')) {
    $tester = new SQLInjectionTester();
    
    $options = getopt('', ['help', 'category:']);
    
    if (isset($options['help'])) {
        echo "SQL Injection Detection Tester\n";
        echo "Usage: php SQLInjectionTester.php [--category=name]\n";
        echo "Categories: patterns, evasion, falsepositives, performance, all\n";
        exit(0);
    }
    
    if (isset($options['category'])) {
        switch ($options['category']) {
            case 'patterns':
                $tester->testSQLPatterns();
                break;
            case 'evasion':
                $tester->testSQLEvasionTechniques();
                break;
            case 'falsepositives':
                $tester->testSQLFalsePositives();
                break;
            case 'performance':
                $tester->testSQLPerformance();
                break;
            case 'all':
            default:
                $result = $tester->runSQLInjectionTests();
                break;
        }
    } else {
        $result = $tester->runSQLInjectionTests();
    }
    
    $tester->cleanup();
    exit(0);
}