<?php

declare(strict_types=1);

namespace PpPhp\Core\Platform\Services\Container;

use PpPhp\Core\Platform\Services\Contracts\ServiceLocatorInterface;
use PpPhp\Core\Platform\Services\Definition\ServiceDefinition;
use PpPhp\Core\Platform\Services\Registry\AliasRegistry;
use PpPhp\Core\Platform\Services\Registry\ServiceRegistry;

final class ServiceContainer implements ServiceLocatorInterface
{
    /** @var array<string, object> */
    private array $instances = [];

    public function __construct(
        private readonly ServiceRegistry $services,
        private readonly AliasRegistry $aliases
    ) {
    }

    public function has(string $id): bool
    {
        $resolved = $this->aliases->resolve($id);

        return $this->services->has($resolved) || isset($this->instances[$resolved]);
    }

    public function get(string $id): object
    {
        $resolved = $this->aliases->resolve($id);

        if (isset($this->instances[$resolved])) {
            return $this->instances[$resolved];
        }

        $definition = $this->services->get($resolved);

        if (!$definition instanceof ServiceDefinition) {
            throw new \RuntimeException('Service not registered: ' . $id);
        }

        if ($definition->hasInstance()) {
            $instance = $definition->instance();
        } elseif ($definition->hasFactory()) {
            $instance = $definition->factory()->create($this);
        } else {
            throw new \RuntimeException('Service has no factory or instance: ' . $resolved);
        }

        if (!is_object($instance)) {
            throw new \RuntimeException('Service factory did not return an object: ' . $resolved);
        }

        if ($definition->metadata()->lifecycle() === 'singleton') {
            $this->instances[$resolved] = $instance;
        }

        return $instance;
    }

    public function setInstance(string $id, object $instance): void
    {
        $this->instances[$id] = $instance;
    }

    public function instances(): array
    {
        return $this->instances;
    }
}
