<?php

namespace App\Agent;

use Illuminate\Contracts\Container\Container;
use InvalidArgumentException;
use Laravel\Ai\Contracts\Tool;

/**
 * Every tool the agent can call.
 *
 * The agent owns no domain logic. Each module registers its own tools from its
 * own service provider, so campaign, creative, landing page and analysis work
 * can proceed in parallel without anyone editing a shared file.
 *
 *     ToolRegistry::register('creative', GenerateImage::class, ListCreatives::class);
 *
 * Names are namespaced (`creative.generate_image`) so two teams can both have
 * a "list" tool without colliding, and so the model can tell from the name
 * alone which part of the product a tool belongs to.
 */
class ToolRegistry
{
    /** @var array<string, list<class-string<Tool>>> */
    private array $tools = [];

    public function __construct(private readonly Container $container) {}

    /**
     * Register one or more tool classes under a namespace.
     *
     * @param  class-string<Tool>  ...$tools
     */
    public function register(string $namespace, string ...$tools): void
    {
        if (! preg_match('/^[a-z][a-z0-9_]*$/', $namespace)) {
            throw new InvalidArgumentException(
                "Tool namespace [{$namespace}] must be lower snake case, for example 'landing_page'."
            );
        }

        foreach ($tools as $tool) {
            if (! is_subclass_of($tool, Tool::class)) {
                throw new InvalidArgumentException(
                    "[{$tool}] must implement ".Tool::class.' to be registered as a tool.'
                );
            }
        }

        $this->tools[$namespace] = [...$this->tools[$namespace] ?? [], ...$tools];
    }

    /** @return list<string> */
    public function namespaces(): array
    {
        return array_keys($this->tools);
    }

    /**
     * Resolve every registered tool, each wrapped so the model sees a
     * namespaced name.
     *
     * @param  list<string>|null  $only  limit to these namespaces
     * @return list<Tool>
     */
    public function resolve(?array $only = null): array
    {
        $resolved = [];

        foreach ($this->tools as $namespace => $classes) {
            if ($only !== null && ! in_array($namespace, $only, true)) {
                continue;
            }

            foreach ($classes as $class) {
                $resolved[] = new NamespacedTool($namespace, $this->container->make($class));
            }
        }

        return $resolved;
    }

    public function count(): int
    {
        return array_sum(array_map('count', $this->tools));
    }
}
