<?php

declare(strict_types=1);

require_once __DIR__ . '/../../vendor/autoload.php';

use App\Framework\DateTime\SystemClock;
use App\Framework\Http\Session\Session;
use App\Framework\Http\Session\SessionId;
use App\Framework\LiveComponents\ComponentEventDispatcher;
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
use App\Framework\LiveComponents\LiveComponentHandler;
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\Random\SecureRandomGenerator;
use App\Framework\Security\CsrfToken;
use App\Framework\Security\CsrfTokenGenerator;
use App\Framework\View\LiveComponentRenderer;
use App\Framework\View\TemplateRenderer;

echo "=== CSRF Integration Test ===\n\n";

// Create real Session instance for testing
$sessionId = SessionId::fromString(bin2hex(random_bytes(16)));
$clock = new SystemClock();
$randomGenerator = new SecureRandomGenerator();
$csrfGenerator = new CsrfTokenGenerator($randomGenerator);

$session = Session::fromArray($sessionId, $clock, $csrfGenerator, []);

// Test 1: Token Generation in Renderer
echo "Test 1: Token Generation in LiveComponentRenderer\n";
echo "------------------------------------------------\n";

$mockTemplateRenderer = new class () implements TemplateRenderer {
    public function render(\App\Framework\View\RenderContext $context): string
    {
        return '';
    }

    public function renderPartial(\App\Framework\View\RenderContext $context): string
    {
        return '';
    }
};

$renderer = new LiveComponentRenderer($mockTemplateRenderer, $session);

$componentId = 'counter:test123';
$html = $renderer->renderWithWrapper(
    $componentId,
    '<div>Counter: 0</div>',
    ['count' => 0]
);

echo "\nGenerated HTML:\n";
echo substr($html, 0, 200) . "...\n";

// Extract token from HTML
if (preg_match('/data-csrf-token="([^"]+)"/', $html, $matches)) {
    $extractedToken = $matches[1];
    echo "\n✓ CSRF token found in HTML: " . substr($extractedToken, 0, 16) . "...\n";
} else {
    echo "\n✗ CSRF token NOT found in HTML!\n";
    exit(1);
}

// Test 2: Token Validation in Handler
echo "\n\nTest 2: Token Validation in LiveComponentHandler\n";
echo "------------------------------------------------\n";

$eventDispatcher = new ComponentEventDispatcher();
$handler = new LiveComponentHandler($eventDispatcher, $session);

// Create test component
$testComponent = new class (ComponentId::fromString($componentId)) implements LiveComponentContract {
    public function __construct(private ComponentId $id, private int $count = 0)
    {
    }

    public function getId(): ComponentId
    {
        return $this->id;
    }

    public function getData(): ComponentData
    {
        return ComponentData::fromArray(['count' => $this->count]);
    }

    public function increment(): ComponentData
    {
        echo "  [Component] Executing increment action\n";
        $this->count++;

        return $this->getData();
    }
};

// Test 2a: Valid Token
echo "\nTest 2a: Valid CSRF Token\n";

try {
    $csrfToken = CsrfToken::fromString($extractedToken);
    $params = ActionParameters::fromArray([], $csrfToken);

    $result = $handler->handle($testComponent, 'increment', $params);

    echo "✓ Action executed successfully!\n";
    echo "  New state: count = " . $result->state->data['count'] . "\n";
} catch (\Exception $e) {
    echo "✗ Failed: " . $e->getMessage() . "\n";
    exit(1);
}

// Test 2b: Missing Token
echo "\nTest 2b: Missing CSRF Token\n";

try {
    $params = ActionParameters::fromArray([]);
    $handler->handle($testComponent, 'increment', $params);

    echo "✗ Should have thrown exception for missing token!\n";
    exit(1);
} catch (\InvalidArgumentException $e) {
    echo "✓ Correctly rejected: " . $e->getMessage() . "\n";
}

// Test 2c: Invalid Token
echo "\nTest 2c: Invalid CSRF Token\n";

try {
    $invalidToken = CsrfToken::fromString(bin2hex(random_bytes(16)));
    $params = ActionParameters::fromArray([], $invalidToken);
    $handler->handle($testComponent, 'increment', $params);

    echo "✗ Should have thrown exception for invalid token!\n";
    exit(1);
} catch (\RuntimeException $e) {
    echo "✓ Correctly rejected: " . $e->getMessage() . "\n";
}

// Test 3: Token Isolation Between Components
echo "\n\nTest 3: Token Isolation Between Components\n";
echo "------------------------------------------------\n";

$componentA = 'counter:instanceA';
$componentB = 'counter:instanceB';

// Render component A
$htmlA = $renderer->renderWithWrapper($componentA, '<div>A</div>', []);
preg_match('/data-csrf-token="([^"]+)"/', $htmlA, $matchesA);
$tokenA = CsrfToken::fromString($matchesA[1]);

echo "Component A token: " . substr($matchesA[1], 0, 16) . "...\n";

// Try to use token A with component B
$testComponentB = new class (ComponentId::fromString($componentB)) implements LiveComponentContract {
    public function __construct(private ComponentId $id)
    {
    }

    public function getId(): ComponentId
    {
        return $this->id;
    }

    public function getData(): ComponentData
    {
        return ComponentData::fromArray([]);
    }

    public function action(): ComponentData
    {
        return $this->getData();
    }
};

try {
    $params = ActionParameters::fromArray([], $tokenA);
    $handler->handle($testComponentB, 'action', $params);

    echo "✗ Should have rejected token from different component!\n";
    exit(1);
} catch (\RuntimeException $e) {
    echo "✓ Correctly isolated: Token from component A rejected by component B\n";
}

// Test 4: End-to-End Flow
echo "\n\nTest 4: Complete End-to-End CSRF Flow\n";
echo "------------------------------------------------\n";

$e2eComponentId = 'counter:e2e';

echo "1. Rendering component...\n";
$htmlE2E = $renderer->renderWithWrapper($e2eComponentId, '<div>E2E Test</div>', ['count' => 0]);

echo "2. Extracting CSRF token from HTML...\n";
preg_match('/data-csrf-token="([^"]+)"/', $htmlE2E, $matchesE2E);
$tokenE2E = CsrfToken::fromString($matchesE2E[1]);

echo "3. Simulating client action with extracted token...\n";
$e2eComponent = new class (ComponentId::fromString($e2eComponentId)) implements LiveComponentContract {
    public function __construct(private ComponentId $id, private int $count = 0)
    {
    }

    public function getId(): ComponentId
    {
        return $this->id;
    }

    public function getData(): ComponentData
    {
        return ComponentData::fromArray(['count' => $this->count]);
    }

    public function increment(): ComponentData
    {
        $this->count++;

        return $this->getData();
    }
};

$paramsE2E = ActionParameters::fromArray([], $tokenE2E);
$resultE2E = $handler->handle($e2eComponent, 'increment', $paramsE2E);

echo "4. Verifying result...\n";
if ($resultE2E->state->data['count'] === 1) {
    echo "✓ E2E Flow completed successfully!\n";
} else {
    echo "✗ E2E Flow failed: unexpected state\n";
    exit(1);
}

echo "\n=== All Tests Passed! ===\n";
echo "\nSummary:\n";
echo "  ✓ Token generation in renderer\n";
echo "  ✓ Token validation in handler\n";
echo "  ✓ Missing token rejection\n";
echo "  ✓ Invalid token rejection\n";
echo "  ✓ Token isolation between components\n";
echo "  ✓ Complete end-to-end flow\n";
echo "\nCSRF Integration is working correctly!\n";
