<?php
declare(strict_types=1);

namespace Tests\Unit\Container;

use Tests\TestCase;
use App\Core\App;
use App\Core\Contracts\RuleEngineInterface;
use App\Core\Contracts\PaymentProcessorInterface;
use App\Core\Contracts\StockManagerInterface;
use App\Core\Contracts\WorkflowEngineInterface;
use App\Core\Contracts\JournalServiceInterface;
use App\Core\Facades\Rules;
use App\Core\Facades\Payment;
use App\Core\Facades\Stock;
use App\Core\Facades\Workflow;
use App\Modules\Rules\RuleEngineAdapter;
use App\Modules\Payments\PaymentProcessorAdapter;
use App\Modules\Inventory\StockManagerAdapter;
use App\Modules\Workflow\WorkflowEngineAdapter;
use App\Modules\Accounting\JournalServiceAdapter;

final class ContainerResolutionTest extends TestCase
{
    private App $app;

    protected function setUp(): void
    {
        parent::setUp();
        $this->app = App::getInstance();

        $this->app->singleton(RuleEngineInterface::class, fn() => new RuleEngineAdapter());
        $this->app->singleton(PaymentProcessorInterface::class, fn() => new PaymentProcessorAdapter());
        $this->app->singleton(StockManagerInterface::class, fn() => new StockManagerAdapter());
        $this->app->singleton(WorkflowEngineInterface::class, fn() => new WorkflowEngineAdapter());
        $this->app->singleton(JournalServiceInterface::class, fn() => new JournalServiceAdapter());
    }

    public function testResolvesRuleEngineInterface(): void
    {
        $service = $this->app->resolve(RuleEngineInterface::class);
        $this->assertInstanceOf(RuleEngineInterface::class, $service);
        $this->assertInstanceOf(RuleEngineAdapter::class, $service);
    }

    public function testResolvesPaymentProcessorInterface(): void
    {
        $service = $this->app->resolve(PaymentProcessorInterface::class);
        $this->assertInstanceOf(PaymentProcessorInterface::class, $service);
        $this->assertInstanceOf(PaymentProcessorAdapter::class, $service);
    }

    public function testResolvesStockManagerInterface(): void
    {
        $service = $this->app->resolve(StockManagerInterface::class);
        $this->assertInstanceOf(StockManagerInterface::class, $service);
        $this->assertInstanceOf(StockManagerAdapter::class, $service);
    }

    public function testResolvesWorkflowEngineInterface(): void
    {
        $service = $this->app->resolve(WorkflowEngineInterface::class);
        $this->assertInstanceOf(WorkflowEngineInterface::class, $service);
        $this->assertInstanceOf(WorkflowEngineAdapter::class, $service);
    }

    public function testResolvesJournalServiceInterface(): void
    {
        $service = $this->app->resolve(JournalServiceInterface::class);
        $this->assertInstanceOf(JournalServiceInterface::class, $service);
        $this->assertInstanceOf(JournalServiceAdapter::class, $service);
    }

    public function testSingletonReturnsSameInstance(): void
    {
        $first = $this->app->resolve(RuleEngineInterface::class);
        $second = $this->app->resolve(RuleEngineInterface::class);
        $this->assertSame($first, $second);
    }

    public function testFacadeResolvesFromContainer(): void
    {
        $instance = Rules::instance();
        $this->assertInstanceOf(RuleEngineInterface::class, $instance);
    }

    public function testPaymentFacadeResolvesFromContainer(): void
    {
        $instance = Payment::instance();
        $this->assertInstanceOf(PaymentProcessorInterface::class, $instance);
    }

    public function testStockFacadeResolvesFromContainer(): void
    {
        $instance = Stock::instance();
        $this->assertInstanceOf(StockManagerInterface::class, $instance);
    }

    public function testWorkflowFacadeResolvesFromContainer(): void
    {
        $instance = Workflow::instance();
        $this->assertInstanceOf(WorkflowEngineInterface::class, $instance);
    }

    public function testBindOverridesSingleton(): void
    {
        $mock = new class implements RuleEngineInterface {
            public function get(string $ruleCode, ?int $branchId = null, ?string $date = null): mixed { return 'mocked'; }
            public function getValue(string $ruleCode, string $key = 'value', ?int $branchId = null): mixed { return 'mocked'; }
            public function getAll(string $category, ?int $branchId = null): array { return []; }
            public function getEffective(string $ruleCode, string $entityType, int $entityId, ?int $branchId = null): mixed { return 'mocked'; }
            public function require(string $ruleCode, ?int $branchId = null): array { return []; }
            public function requireValue(string $ruleCode, string $key = 'value', ?int $branchId = null): mixed { return 'mocked'; }
        };

        $this->app->bind(RuleEngineInterface::class, $mock);
        $resolved = $this->app->resolve(RuleEngineInterface::class);
        $this->assertSame($mock, $resolved);
    }

    public function testResolveReturnsDefaultWhenNotBound(): void
    {
        $result = $this->app->resolve('nonexistent.service', 'default_value');
        $this->assertEquals('default_value', $result);
    }
}
