<?php

namespace PloverCRM\Core;

use Exception;

/**
 * Simple Dependency Injection Container
 * 
 * Manages service registration and resolution
 */
class Container
{
    /**
     * @var array Registered services
     */
    private $services = [];

    /**
     * @var array Resolved singleton instances
     */
    private $instances = [];

    /**
     * Register a service
     * 
     * @param string $id Service identifier
     * @param callable|object $concrete Service factory or instance
     * @param bool $singleton Whether to cache the resolved instance
     * @return void
     */
    public function register(string $id, $concrete, bool $singleton = true): void
    {
        $this->services[$id] = [
            'concrete' => $concrete,
            'singleton' => $singleton,
        ];
    }

    /**
     * Register a singleton service
     * 
     * @param string $id Service identifier
     * @param callable|object $concrete Service factory or instance
     * @return void
     */
    public function singleton(string $id, $concrete): void
    {
        $this->register($id, $concrete, true);
    }

    /**
     * Register a factory service (new instance each time)
     * 
     * @param string $id Service identifier
     * @param callable $concrete Service factory
     * @return void
     */
    public function factory(string $id, callable $concrete): void
    {
        $this->register($id, $concrete, false);
    }

    /**
     * Resolve a service from the container
     * 
     * @param string $id Service identifier
     * @return mixed
     * @throws Exception
     */
    public function get(string $id)
    {
        // Return cached singleton instance if exists
        if (isset($this->instances[$id])) {
            return $this->instances[$id];
        }

        // Check if service is registered
        if (!isset($this->services[$id])) {
            throw new Exception("Service '{$id}' not found in container.");
        }

        $service = $this->services[$id];
        $concrete = $service['concrete'];

        // Resolve the service
        if (is_callable($concrete)) {
            $instance = $concrete($this);
        } else {
            $instance = $concrete;
        }

        // Cache singleton instances
        if ($service['singleton']) {
            $this->instances[$id] = $instance;
        }

        return $instance;
    }

    /**
     * Check if a service is registered
     * 
     * @param string $id Service identifier
     * @return bool
     */
    public function has(string $id): bool
    {
        return isset($this->services[$id]);
    }

    /**
     * Remove a service from the container
     * 
     * @param string $id Service identifier
     * @return void
     */
    public function remove(string $id): void
    {
        unset($this->services[$id], $this->instances[$id]);
    }

    /**
     * Get all registered service IDs
     * 
     * @return array
     */
    public function getServiceIds(): array
    {
        return array_keys($this->services);
    }
}
