<?php

declare(strict_types=1);

use App\Framework\Config\AppConfig;
use App\Framework\Config\DiscoveryConfig;
use App\Framework\Config\External\ExternalApiConfig;
use App\Framework\Config\SecurityConfig;
use App\Framework\Config\TypedConfiguration;
use App\Framework\Core\Application;
use App\Framework\Core\Events\EventDispatcherInterface;
use App\Framework\Database\Config\DatabaseConfig;
use App\Framework\DI\Container;
use App\Framework\DI\DefaultContainer;
use App\Framework\Http\HttpMiddlewareChain;
use App\Framework\Http\HttpMiddlewareChainInterface;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\HttpResponse;
use App\Framework\Http\Method;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\MiddlewareManagerInterface;
use App\Framework\Http\Request;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\ResponseEmitter;
use App\Framework\Http\Status;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\Logger;
use App\Framework\RateLimit\RateLimitConfig;
use App\Framework\Router\HttpRouter;

// Simple test doubles
class TestEventDispatcher implements EventDispatcherInterface
{
    public array $dispatched = [];

    public function dispatch(object $event): array
    {
        $this->dispatched[] = $event;

        return [];
    }
}

class TestMiddleware
{
    public function __invoke(MiddlewareContext $context, HttpMiddlewareChainInterface $next, RequestStateManager $stateManager): MiddlewareContext
    {
        $response = new HttpResponse(Status::OK, [], 'Test Response');

        return new MiddlewareContext($context->request, $response);
    }
}

class TestMiddlewareManager implements MiddlewareManagerInterface
{
    public HttpMiddlewareChain $chain;

    public function __construct(Container $container)
    {
        // Register the test middleware class in container
        $container->bind(TestMiddleware::class, new TestMiddleware());

        // Create the real chain with minimal middlewares
        $this->chain = new HttpMiddlewareChain(
            [TestMiddleware::class],
            $container
        );
    }
}

beforeEach(function () {
    $this->container = new DefaultContainer();

    // Create a minimal test database config
    $driverConfig = new \App\Framework\Database\Driver\DriverConfig(
        driverType: \App\Framework\Database\Driver\DriverType::SQLITE,
        host: 'localhost',
        port: 0,
        database: ':memory:',
        username: '',
        password: '',
        charset: 'utf8mb4'
    );
    $poolConfig = new \App\Framework\Database\Config\PoolConfig(
        enabled: false,
        maxConnections: 10,
        minConnections: 1
    );
    $readWriteConfig = new \App\Framework\Database\Config\ReadWriteConfig(
        enabled: false
    );
    $databaseConfig = new DatabaseConfig($driverConfig, $poolConfig, $readWriteConfig);

    $this->config = new TypedConfiguration(
        database: $databaseConfig,
        app: new AppConfig(
            name: 'Test App',
            version: '1.0.0-test',
            environment: 'testing',
            debug: true,
            timezone: \App\Framework\DateTime\Timezone::UTC
        ),
        security: new SecurityConfig(
            appKey: 'test',
            enableSecurityHeaders: false,
            enableCsrfProtection: false,
            enableRateLimiting: false
        ),
        rateLimit: RateLimitConfig::testing(),
        externalApis: new ExternalApiConfig(
            shopify: new \App\Framework\Config\External\ShopifyConfig('', '', '', '', false),
            rapidMail: new \App\Framework\Config\External\RapidMailConfig('', '', true)
        ),
        discovery: new DiscoveryConfig()
    );

    $this->responseEmitter = new ResponseEmitter();

    // Register essential dependencies in container
    $this->container->bind(Logger::class, new DefaultLogger());
    $this->container->bind(HttpRouter::class, new class () {});
    $this->container->bind(\App\Framework\Cache\Cache::class, new \App\Framework\Cache\GeneralCache(new \App\Framework\Cache\Driver\InMemoryCache(), new \App\Framework\Serializer\Php\PhpSerializer()));

    // Register Request for handleRequest
    $this->container->bind(Request::class, new HttpRequest(
        method: Method::GET,
        path: '/test'
    ));

    // Create test doubles
    $this->middlewareManager = new TestMiddlewareManager($this->container);
    $this->eventDispatcher = new TestEventDispatcher();

    $this->application = new Application(
        $this->container,
        $this->responseEmitter,
        $this->config,
        $this->middlewareManager,
        $this->eventDispatcher
    );
});

it('creates application with dependencies', function () {
    expect($this->application)->toBeInstanceOf(Application::class);
});

it('gets config values correctly', function () {
    expect($this->application->config('environment'))->toBe('testing');
    expect($this->application->config('app.version'))->toBe('1.0.0-test');
    expect($this->application->config('nonexistent', 'default'))->toBe('default');
    expect($this->application->config('nonexistent'))->toBeNull();
});

it('can be instantiated with test doubles', function () {
    // Test that Application can be created with our test doubles
    // This verifies the interface extraction works for dependency injection
    expect($this->application)->toBeInstanceOf(Application::class);
    expect($this->middlewareManager)->toBeInstanceOf(MiddlewareManagerInterface::class);
    expect($this->eventDispatcher)->toBeInstanceOf(EventDispatcherInterface::class);
});

it('verifies interface extraction allows dependency injection with test doubles', function () {
    // This test verifies that our interface extraction allows the Application
    // to be tested without requiring the full container setup.
    // The fact that we can instantiate it with our simple test doubles
    // proves that the refactoring achieved its goal.

    expect($this->application)->toBeInstanceOf(Application::class);

    // Verify our test doubles implement the interfaces
    expect($this->middlewareManager)->toBeInstanceOf(MiddlewareManagerInterface::class);
    expect($this->eventDispatcher)->toBeInstanceOf(EventDispatcherInterface::class);

    // Verify the Application is using our test doubles (not container-resolved instances)
    expect($this->application->config('environment'))->toBe('testing');
});
