<?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\Attributes\RequiresPermission;
use App\Framework\LiveComponents\ComponentEventDispatcher;
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
use App\Framework\LiveComponents\Exceptions\UnauthorizedActionException;
use App\Framework\LiveComponents\LiveComponentHandler;
use App\Framework\LiveComponents\Security\SessionBasedAuthorizationChecker;
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;
use App\Framework\Random\SecureRandomGenerator;
use App\Framework\Security\CsrfTokenGenerator;

echo "=== Action Authorization Test ===\n\n";

// Setup Session
$sessionId = SessionId::fromString(bin2hex(random_bytes(16)));
$clock = new SystemClock();
$randomGenerator = new SecureRandomGenerator();
$csrfGenerator = new CsrfTokenGenerator($randomGenerator);

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

// Setup Handler
$eventDispatcher = new ComponentEventDispatcher();
$authChecker = new SessionBasedAuthorizationChecker($session);
$handler = new LiveComponentHandler($eventDispatcher, $session, $authChecker);

// Test Component with protected action
$componentId = ComponentId::fromString('posts:manager');

$component = new class ($componentId) implements LiveComponentContract {
    public function __construct(private ComponentId $id)
    {
    }

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

    public function getData(): ComponentData
    {
        return ComponentData::fromArray(['posts' => []]);
    }

    public function getRenderData(): ComponentRenderData
    {
        return new ComponentRenderData('posts-manager', ['posts' => []]);
    }

    // Public action - no permission required
    public function viewPosts(): ComponentData
    {
        return ComponentData::fromArray(['posts' => ['Post 1', 'Post 2']]);
    }

    // Protected action - requires permission
    #[RequiresPermission('posts.delete')]
    public function deletePost(string $postId): ComponentData
    {
        return ComponentData::fromArray([
            'deleted' => true,
            'postId' => $postId,
        ]);
    }

    // Multiple permissions (OR logic)
    #[RequiresPermission('posts.edit', 'posts.admin')]
    public function editPost(string $postId): ComponentData
    {
        return ComponentData::fromArray([
            'edited' => true,
            'postId' => $postId,
        ]);
    }
};

// Generate CSRF token
$formId = 'livecomponent:' . $componentId->toString();
$csrfToken = $session->csrf->generateToken($formId);

// Test 1: Public action without authentication
echo "Test 1: Public action without authentication\n";
echo "--------------------------------------------\n";

try {
    $params = ActionParameters::fromArray([], $csrfToken);
    $result = $handler->handle($component, 'viewPosts', $params);
    echo "✓ Public action executed successfully\n";
    echo "  Posts: " . json_encode($result->state->data['posts']) . "\n";
} catch (\Exception $e) {
    echo "✗ Failed: " . $e->getMessage() . "\n";
    exit(1);
}

// Test 2: Protected action without authentication
echo "\nTest 2: Protected action without authentication\n";
echo "------------------------------------------------\n";

try {
    $params = ActionParameters::fromArray(['postId' => '123'], $csrfToken);
    $handler->handle($component, 'deletePost', $params);
    echo "✗ Should have thrown UnauthorizedActionException!\n";
    exit(1);
} catch (UnauthorizedActionException $e) {
    echo "✓ Correctly rejected unauthenticated user\n";
    echo "  Error: " . $e->getUserMessage() . "\n";
    echo "  Is authentication issue: " . ($e->isAuthenticationIssue() ? 'yes' : 'no') . "\n";
}

// Test 3: Protected action with authentication but missing permission
echo "\nTest 3: Protected action with permission check (missing permission)\n";
echo "--------------------------------------------------------------------\n";
$session->set('user', [
    'id' => 123,
    'permissions' => ['posts.view', 'posts.edit'],  // No 'posts.delete'
]);

try {
    $params = ActionParameters::fromArray(['postId' => '456'], $csrfToken);
    $handler->handle($component, 'deletePost', $params);
    echo "✗ Should have thrown UnauthorizedActionException!\n";
    exit(1);
} catch (UnauthorizedActionException $e) {
    echo "✓ Correctly rejected user without permission\n";
    echo "  Error: " . $e->getUserMessage() . "\n";
    echo "  Missing permissions: " . json_encode($e->getMissingPermissions()) . "\n";
}

// Test 4: Protected action with correct permission
echo "\nTest 4: Protected action with correct permission\n";
echo "------------------------------------------------\n";
$session->set('user', [
    'id' => 123,
    'permissions' => ['posts.view', 'posts.edit', 'posts.delete'],
]);

try {
    $params = ActionParameters::fromArray(['postId' => '789'], $csrfToken);
    $result = $handler->handle($component, 'deletePost', $params);
    echo "✓ Action executed successfully with permission\n";
    echo "  Result: " . json_encode($result->state->data) . "\n";
} catch (\Exception $e) {
    echo "✗ Failed: " . $e->getMessage() . "\n";
    exit(1);
}

// Test 5: Multiple permissions (OR logic)
echo "\nTest 5: Multiple permissions with OR logic\n";
echo "-------------------------------------------\n";
$session->set('user', [
    'id' => 123,
    'permissions' => ['posts.admin'],  // Has 'posts.admin', not 'posts.edit'
]);

try {
    $params = ActionParameters::fromArray(['postId' => '999'], $csrfToken);
    $result = $handler->handle($component, 'editPost', $params);
    echo "✓ Action executed with alternative permission\n";
    echo "  User has 'posts.admin' instead of 'posts.edit'\n";
    echo "  Result: " . json_encode($result->state->data) . "\n";
} catch (\Exception $e) {
    echo "✗ Failed: " . $e->getMessage() . "\n";
    exit(1);
}

// Test 6: RequiresPermission attribute validation
echo "\nTest 6: RequiresPermission attribute behavior\n";
echo "----------------------------------------------\n";
$attr1 = new RequiresPermission('posts.edit');
echo "Single permission attribute:\n";
echo "  Permissions: " . json_encode($attr1->getPermissions()) . "\n";
echo "  Primary: " . $attr1->getPrimaryPermission() . "\n";
echo "  Has multiple: " . ($attr1->hasMultiplePermissions() ? 'yes' : 'no') . "\n";

$attr2 = new RequiresPermission('posts.edit', 'posts.admin');
echo "\nMultiple permissions attribute:\n";
echo "  Permissions: " . json_encode($attr2->getPermissions()) . "\n";
echo "  Primary: " . $attr2->getPrimaryPermission() . "\n";
echo "  Has multiple: " . ($attr2->hasMultiplePermissions() ? 'yes' : 'no') . "\n";

echo "\nPermission checking:\n";
echo "  User with ['posts.edit']: " . ($attr2->isAuthorized(['posts.edit']) ? 'authorized' : 'denied') . "\n";
echo "  User with ['posts.admin']: " . ($attr2->isAuthorized(['posts.admin']) ? 'authorized' : 'denied') . "\n";
echo "  User with ['posts.view']: " . ($attr2->isAuthorized(['posts.view']) ? 'authorized' : 'denied') . "\n";

echo "\n=== All Tests Passed! ===\n";
echo "\nSummary:\n";
echo "  ✓ Public actions work without authentication\n";
echo "  ✓ Protected actions require authentication\n";
echo "  ✓ Permission checks work correctly\n";
echo "  ✓ Missing permissions are rejected\n";
echo "  ✓ Multiple permissions support OR logic\n";
echo "  ✓ RequiresPermission attribute works as expected\n";
echo "\nAction Guards Implementation: COMPLETE\n";
