<?php

declare(strict_types=1);

use App\Framework\Attributes\Route;
use App\Framework\Core\RouteCompiler;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Core\ValueObjects\MethodName;
use App\Framework\Discovery\ValueObjects\AttributeTarget;
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
use App\Framework\Http\Headers;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\ServerEnvironment;
use App\Framework\Router\HttpRouter;
use App\Framework\Router\RouteMatchSuccess;

it('compiles routes with exact subdomain patterns', function () {
    $compiler = new RouteCompiler();

    // Create a discovered route with exact subdomain
    $discoveredRoute = new DiscoveredAttribute(
        className: ClassName::create('TestController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('apiEndpoint'),
        arguments: [
            'path' => '/api/test',
            'method' => Method::GET,
            'subdomain' => 'api',
        ],
        additionalData: ['parameters' => []]
    );

    $compiled = $compiler->compile($discoveredRoute);

    expect($compiled)->toHaveKey('GET')
        ->and($compiled['GET'])->toHaveKey('exact:api')
        ->and($compiled['GET']['exact:api']['static'])->toHaveKey('/api/test');
});

it('compiles routes with wildcard subdomain patterns', function () {
    $compiler = new RouteCompiler();

    $discoveredRoute = new DiscoveredAttribute(
        className: ClassName::create('TestController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('wildcardEndpoint'),
        arguments: [
            'path' => '/wildcard',
            'method' => Method::GET,
            'subdomain' => '*.app',
        ],
        additionalData: ['parameters' => []]
    );

    $compiled = $compiler->compile($discoveredRoute);

    expect($compiled)->toHaveKey('GET')
        ->and($compiled['GET'])->toHaveKey('wildcard:*.app')
        ->and($compiled['GET']['wildcard:*.app']['static'])->toHaveKey('/wildcard');
});

it('compiles routes with multiple subdomain patterns', function () {
    $compiler = new RouteCompiler();

    $discoveredRoute = new DiscoveredAttribute(
        className: ClassName::create('TestController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('multiSubdomain'),
        arguments: [
            'path' => '/multi',
            'method' => Method::GET,
            'subdomain' => ['api', 'admin'],
        ],
        additionalData: ['parameters' => []]
    );

    $compiled = $compiler->compile($discoveredRoute);

    expect($compiled['GET'])->toHaveKey('exact:api')
        ->and($compiled['GET'])->toHaveKey('exact:admin')
        ->and($compiled['GET']['exact:api']['static'])->toHaveKey('/multi')
        ->and($compiled['GET']['exact:admin']['static'])->toHaveKey('/multi');
});

it('falls back to default routes when no subdomain specified', function () {
    $compiler = new RouteCompiler();

    $discoveredRoute = new DiscoveredAttribute(
        className: ClassName::create('TestController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('defaultRoute'),
        arguments: [
            'path' => '/default',
            'method' => Method::GET,
            'subdomain' => [],
        ],
        additionalData: ['parameters' => []]
    );

    $compiled = $compiler->compile($discoveredRoute);

    expect($compiled)->toHaveKey('GET')
        ->and($compiled['GET'])->toHaveKey('default')
        ->and($compiled['GET']['default']['static'])->toHaveKey('/default');
});

it('router matches exact subdomain routes correctly', function () {
    $compiler = new RouteCompiler();

    // Create routes for different subdomains
    $apiRoute = new DiscoveredAttribute(
        className: ClassName::create('ApiController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('index'),
        arguments: [
            'path' => '/test',
            'method' => Method::GET,
            'subdomain' => 'api',
        ],
        additionalData: ['parameters' => []]
    );

    $defaultRoute = new DiscoveredAttribute(
        className: ClassName::create('WebController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('index'),
        arguments: [
            'path' => '/test',
            'method' => Method::GET,
            'subdomain' => [],
        ],
        additionalData: ['parameters' => []]
    );

    $compiledRoutes = $compiler->compileOptimized($apiRoute, $defaultRoute);
    $router = new HttpRouter($compiledRoutes);

    // Test API subdomain request
    $apiRequest = new HttpRequest(
        method: Method::GET,
        path: '/test',
        headers: new Headers(['Host' => 'api.example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'api.example.com'])
    );

    $apiContext = $router->match($apiRequest);
    expect($apiContext->match)->toBeInstanceOf(RouteMatchSuccess::class);

    if ($apiContext->match instanceof RouteMatchSuccess) {
        expect($apiContext->match->route->controller)->toBe('ApiController');
    }

    // Test default domain request
    $webRequest = new HttpRequest(
        method: Method::GET,
        path: '/test',
        headers: new Headers(['Host' => 'example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'example.com'])
    );

    $webContext = $router->match($webRequest);
    expect($webContext->match)->toBeInstanceOf(RouteMatchSuccess::class);

    if ($webContext->match instanceof RouteMatchSuccess) {
        expect($webContext->match->route->controller)->toBe('WebController');
    }
});

it('router matches wildcard subdomain patterns', function () {
    $compiler = new RouteCompiler();

    $wildcardRoute = new DiscoveredAttribute(
        className: ClassName::create('TenantController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('dashboard'),
        arguments: [
            'path' => '/dashboard',
            'method' => Method::GET,
            'subdomain' => '*.app',
        ],
        additionalData: ['parameters' => []]
    );

    $compiledRoutes = $compiler->compileOptimized($wildcardRoute);
    $router = new HttpRouter($compiledRoutes);

    // Test wildcard match
    $tenantRequest = new HttpRequest(
        method: Method::GET,
        path: '/dashboard',
        headers: new Headers(['Host' => 'tenant1.app.example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'tenant1.app.example.com'])
    );

    $context = $router->match($tenantRequest);
    expect($context->match)->toBeInstanceOf(RouteMatchSuccess::class);

    if ($context->match instanceof RouteMatchSuccess) {
        expect($context->match->route->controller)->toBe('TenantController');
    }
});

it('router prioritizes exact subdomain over wildcard patterns', function () {
    $compiler = new RouteCompiler();

    $exactRoute = new DiscoveredAttribute(
        className: ClassName::create('ExactController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('special'),
        arguments: [
            'path' => '/special',
            'method' => Method::GET,
            'subdomain' => 'admin',
        ],
        additionalData: ['parameters' => []]
    );

    $wildcardRoute = new DiscoveredAttribute(
        className: ClassName::create('WildcardController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('general'),
        arguments: [
            'path' => '/special',
            'method' => Method::GET,
            'subdomain' => '*',
        ],
        additionalData: ['parameters' => []]
    );

    $compiledRoutes = $compiler->compileOptimized($exactRoute, $wildcardRoute);
    $router = new HttpRouter($compiledRoutes);

    // Test that exact match takes precedence
    $request = new HttpRequest(
        method: Method::GET,
        path: '/special',
        headers: new Headers(['Host' => 'admin.example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'admin.example.com'])
    );

    $context = $router->match($request);
    expect($context->match)->toBeInstanceOf(RouteMatchSuccess::class);

    if ($context->match instanceof RouteMatchSuccess) {
        expect($context->match->route->controller)->toBe('ExactController');
    }
});

it('compiles dynamic routes with subdomain patterns', function () {
    $compiler = new RouteCompiler();

    $dynamicRoute = new DiscoveredAttribute(
        className: ClassName::create('ApiController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('getUser'),
        arguments: [
            'path' => '/api/users/{id}',
            'method' => Method::GET,
            'subdomain' => 'api',
        ],
        additionalData: ['parameters' => []]
    );

    $compiled = $compiler->compile($dynamicRoute);

    expect($compiled['GET']['exact:api']['dynamic'])->toHaveCount(1)
        ->and($compiled['GET']['exact:api']['dynamic'][0]->path)->toBe('/api/users/{id}')
        ->and($compiled['GET']['exact:api']['dynamic'][0]->paramNames)->toBe(['id']);
});

it('extracts subdomain correctly from various host patterns', function () {
    $compiler = new RouteCompiler();

    $testRoute = new DiscoveredAttribute(
        className: ClassName::create('TestController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('test'),
        arguments: [
            'path' => '/test',
            'method' => Method::GET,
            'subdomain' => 'api',
        ],
        additionalData: ['parameters' => []]
    );

    $compiledRoutes = $compiler->compileOptimized($testRoute);
    $router = new HttpRouter($compiledRoutes);

    // Test various host patterns
    $testCases = [
        'api.example.com' => 'api',
        'www.example.com' => '', // www is ignored
        'example.com' => '',      // no subdomain
        'sub.api.example.com' => 'sub.api', // full subdomain part
        'test.localhost' => 'test', // development domain
        'localhost' => '', // main localhost
    ];

    foreach ($testCases as $host => $expectedSubdomain) {
        $request = new HttpRequest(
            method: Method::GET,
            path: '/test',
            headers: new Headers(['Host' => $host]),
            server: new ServerEnvironment(['HTTP_HOST' => $host])
        );

        // Use reflection to test the private extractSubdomain method
        $reflection = new ReflectionClass($router);
        $method = $reflection->getMethod('extractSubdomain');
        $method->setAccessible(true);

        $result = $method->invoke($router, $host);
        expect($result)->toBe($expectedSubdomain, "Failed for host: $host");
    }
});

it('default routes are NOT accessible via subdomain', function () {
    $compiler = new RouteCompiler();

    // Create a default route (no subdomain specified)
    $defaultRoute = new DiscoveredAttribute(
        className: ClassName::create('HomeController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('index'),
        arguments: [
            'path' => '/home',
            'method' => Method::GET,
            'subdomain' => [],  // No subdomain = default route
        ],
        additionalData: ['parameters' => []]
    );

    $compiledRoutes = $compiler->compileOptimized($defaultRoute);
    $router = new HttpRouter($compiledRoutes);

    // Test 1: Should work on main domain (no subdomain)
    $mainDomainRequest = new HttpRequest(
        method: Method::GET,
        path: '/home',
        headers: new Headers(['Host' => 'example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'example.com'])
    );

    $mainContext = $router->match($mainDomainRequest);
    expect($mainContext->match)->toBeInstanceOf(RouteMatchSuccess::class);

    // Test 2: Should NOT work on subdomain
    $subdomainRequest = new HttpRequest(
        method: Method::GET,
        path: '/home',
        headers: new Headers(['Host' => 'api.example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'api.example.com'])
    );

    $subdomainContext = $router->match($subdomainRequest);
    expect($subdomainContext->match)->toBeInstanceOf(\App\Framework\Router\NoRouteMatch::class);
});

it('subdomain-specific routes are NOT accessible via main domain', function () {
    $compiler = new RouteCompiler();

    // Create a subdomain-specific route
    $apiRoute = new DiscoveredAttribute(
        className: ClassName::create('ApiController'),
        attributeClass: Route::class,
        target: AttributeTarget::METHOD,
        methodName: MethodName::create('endpoint'),
        arguments: [
            'path' => '/api/data',
            'method' => Method::GET,
            'subdomain' => 'api',  // Only accessible via api.domain.com
        ],
        additionalData: ['parameters' => []]
    );

    $compiledRoutes = $compiler->compileOptimized($apiRoute);
    $router = new HttpRouter($compiledRoutes);

    // Test 1: Should work on correct subdomain
    $correctSubdomainRequest = new HttpRequest(
        method: Method::GET,
        path: '/api/data',
        headers: new Headers(['Host' => 'api.example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'api.example.com'])
    );

    $correctContext = $router->match($correctSubdomainRequest);
    expect($correctContext->match)->toBeInstanceOf(RouteMatchSuccess::class);

    // Test 2: Should NOT work on main domain
    $mainDomainRequest = new HttpRequest(
        method: Method::GET,
        path: '/api/data',
        headers: new Headers(['Host' => 'example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'example.com'])
    );

    $mainContext = $router->match($mainDomainRequest);
    expect($mainContext->match)->toBeInstanceOf(\App\Framework\Router\NoRouteMatch::class);

    // Test 3: Should NOT work on wrong subdomain
    $wrongSubdomainRequest = new HttpRequest(
        method: Method::GET,
        path: '/api/data',
        headers: new Headers(['Host' => 'admin.example.com']),
        server: new ServerEnvironment(['HTTP_HOST' => 'admin.example.com'])
    );

    $wrongContext = $router->match($wrongSubdomainRequest);
    expect($wrongContext->match)->toBeInstanceOf(\App\Framework\Router\NoRouteMatch::class);
});
