<?php

namespace Tests\Unit;

use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use App\Http\Requests\ProfileUpdateRequest;
use Illuminate\Foundation\Testing\WithFaker;
use PHPUnit\Framework\TestCase;
use ReflectionClass;

class InputSanitizationTest extends TestCase
{
    use WithFaker;

    /**
     * Test XSS sanitization in StoreUserRequest.
     */
    public function test_store_user_request_sanitizes_xss_payloads(): void
    {
        $request = new StoreUserRequest();
        
        // Use reflection to access private sanitizeInput method
        $reflection = new ReflectionClass($request);
        $method = $reflection->getMethod('sanitizeInput');
        $method->setAccessible(true);

        // Test various XSS payloads
        $xssPayloads = [
            '<script>alert("XSS")</script>' => 'scriptalert("XSS")/script',
            '<img src="x" onerror="alert(1)">' => 'img src="x" ="alert(1)"',
            'javascript:alert("XSS")' => 'alert("XSS")',
            '<svg onload="alert(1)">' => 'svg ="alert(1)"',
            '"><script>alert("XSS")</script>' => 'scriptalert("XSS")/script',
            '<iframe src="javascript:alert(1)"></iframe>' => 'iframe src="alert(1)"/iframe',
            'data:text/html,<script>alert(1)</script>' => 'text/html,scriptalert(1)/script',
        ];

        foreach ($xssPayloads as $payload => $expected) {
            $sanitized = $method->invoke($request, $payload);
            
            // Verify dangerous elements are removed
            $this->assertStringNotContainsString('<script>', $sanitized);
            $this->assertStringNotContainsString('javascript:', $sanitized);
            $this->assertStringNotContainsString('data:', $sanitized);
            $this->assertStringNotContainsString('onerror=', $sanitized);
            $this->assertStringNotContainsString('onload=', $sanitized);
            $this->assertStringNotContainsString('<', $sanitized);
            $this->assertStringNotContainsString('>', $sanitized);
            $this->assertStringNotContainsString('"', $sanitized);
            $this->assertStringNotContainsString("'", $sanitized);
            $this->assertStringNotContainsString('&', $sanitized);
        }
    }

    /**
     * Test XSS sanitization in UpdateUserRequest.
     */
    public function test_update_user_request_sanitizes_xss_payloads(): void
    {
        $request = new UpdateUserRequest();
        
        // Use reflection to access private sanitizeInput method
        $reflection = new ReflectionClass($request);
        $method = $reflection->getMethod('sanitizeInput');
        $method->setAccessible(true);

        $xssPayload = '<script>alert("XSS")</script>';
        $sanitized = $method->invoke($request, $xssPayload);
        
        // Verify dangerous elements are removed
        $this->assertStringNotContainsString('<script>', $sanitized);
        $this->assertStringNotContainsString('</script>', $sanitized);
        $this->assertStringNotContainsString('<', $sanitized);
        $this->assertStringNotContainsString('>', $sanitized);
    }

    /**
     * Test XSS sanitization in ProfileUpdateRequest.
     */
    public function test_profile_update_request_sanitizes_xss_payloads(): void
    {
        $request = new ProfileUpdateRequest();
        
        // Use reflection to access private sanitizeInput method
        $reflection = new ReflectionClass($request);
        $method = $reflection->getMethod('sanitizeInput');
        $method->setAccessible(true);

        $xssPayload = '<img src="x" onerror="alert(1)">';
        $sanitized = $method->invoke($request, $xssPayload);
        
        // Verify dangerous elements are removed
        $this->assertStringNotContainsString('<img', $sanitized);
        $this->assertStringNotContainsString('onerror=', $sanitized);
        $this->assertStringNotContainsString('alert', $sanitized);
        $this->assertStringNotContainsString('<', $sanitized);
        $this->assertStringNotContainsString('>', $sanitized);
    }

    /**
     * Test that legitimate content is preserved after sanitization.
     */
    public function test_legitimate_content_handling(): void
    {
        $request = new StoreUserRequest();
        
        // Use reflection to access private sanitizeInput method
        $reflection = new ReflectionClass($request);
        $method = $reflection->getMethod('sanitizeInput');
        $method->setAccessible(true);

        // Test legitimate content (note: our sanitization is aggressive and removes some characters)
        $legitimateInputs = [
            'John Doe' => 'John Doe',
            'john.doe@example.com' => 'john.doe@example.com',
            'Company Name Inc.' => 'Company Name Inc.',
            'Test User 123' => 'Test User 123',
        ];

        foreach ($legitimateInputs as $input => $expected) {
            $sanitized = $method->invoke($request, $input);
            $this->assertEquals($expected, $sanitized);
        }
    }

    /**
     * Test that empty and null inputs are handled properly.
     */
    public function test_empty_inputs_handling(): void
    {
        $request = new StoreUserRequest();
        
        // Use reflection to access private sanitizeInput method
        $reflection = new ReflectionClass($request);
        $method = $reflection->getMethod('sanitizeInput');
        $method->setAccessible(true);

        // Test empty string
        $sanitized = $method->invoke($request, '');
        $this->assertEquals('', $sanitized);

        // Test whitespace only
        $sanitized = $method->invoke($request, '   ');
        $this->assertEquals('', $sanitized);

        // Test string with only dangerous characters
        $sanitized = $method->invoke($request, '<>"\'"&');
        $this->assertEquals('', $sanitized);
    }

    /**
     * Test specific XSS attack vectors.
     */
    public function test_specific_xss_vectors(): void
    {
        $request = new StoreUserRequest();
        
        // Use reflection to access private sanitizeInput method
        $reflection = new ReflectionClass($request);
        $method = $reflection->getMethod('sanitizeInput');
        $method->setAccessible(true);

        $xssVectors = [
            // Script injection
            '<script>document.cookie</script>',
            // Event handler injection
            '<div onclick="alert(1)">',
            // JavaScript protocol
            'javascript:void(0)',
            // Data URI
            'data:text/html,<script>alert(1)</script>',
            // SVG injection
            '<svg/onload=alert(1)>',
            // IMG injection
            '<img src=x onerror=alert(1)>',
            // Style injection
            '<style>body{background:url("javascript:alert(1)")}</style>',
        ];

        foreach ($xssVectors as $vector) {
            $sanitized = $method->invoke($request, $vector);
            
            // Verify all dangerous elements are removed
            $this->assertStringNotContainsString('script', $sanitized);
            $this->assertStringNotContainsString('javascript:', $sanitized);
            $this->assertStringNotContainsString('data:', $sanitized);
            $this->assertStringNotContainsString('onclick', $sanitized);
            $this->assertStringNotContainsString('onload', $sanitized);
            $this->assertStringNotContainsString('onerror', $sanitized);
            $this->assertStringNotContainsString('<', $sanitized);
            $this->assertStringNotContainsString('>', $sanitized);
        }
    }
}