<?php

namespace App\Core;

class Container
{
    private array $bindings = [];
    private array $instances = [];

    public function singleton(string $id, callable $factory): void
    {
        $this->bindings[$id] = ['factory' => $factory, 'singleton' => true];
    }

    public function bind(string $id, callable $factory): void
    {
        $this->bindings[$id] = ['factory' => $factory, 'singleton' => false];
    }

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

    public function get(string $id): mixed
    {
        if (array_key_exists($id, $this->instances)) {
            return $this->instances[$id];
        }

        if (!array_key_exists($id, $this->bindings)) {
            if (class_exists($id)) {
                return $this->build($id);
            }

            throw new \RuntimeException("Container binding not found: {$id}");
        }

        $binding = $this->bindings[$id];
        $object = $binding['factory']($this);

        if ($binding['singleton']) {
            $this->instances[$id] = $object;
        }

        return $object;
    }

    private function build(string $class): object
    {
        $ref = new \ReflectionClass($class);
        $constructor = $ref->getConstructor();

        if (!$constructor) {
            return new $class();
        }

        $args = [];
        foreach ($constructor->getParameters() as $parameter) {
            $type = $parameter->getType();
            if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
                $args[] = $this->get($type->getName());
                continue;
            }

            if ($parameter->isDefaultValueAvailable()) {
                $args[] = $parameter->getDefaultValue();
                continue;
            }

            throw new \RuntimeException("Cannot resolve {$class}::\${$parameter->getName()}");
        }

        return $ref->newInstanceArgs($args);
    }
}
