<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\Tools;

use Plugin\tnt_mcp\src\Mcp\Contracts\Tool;
use Plugin\tnt_mcp\src\Mcp\Mode\EndpointClass;
use Plugin\tnt_mcp\src\Ai\AiConfigurationStore;
use Plugin\tnt_mcp\src\Ai\AiConnectionTester;

/**
 * READ tool: the models the stored key may actually use (epic 4.3).
 *
 * The list comes from the PROVIDER, not from a table in this plugin. A hardcoded catalogue would be
 * wrong within weeks — models are added and retired continuously, and account tiers differ in what
 * they may call — so a client that picked from it would confidently configure a model this shop
 * cannot use. Asking the provider costs one round trip and is always right for THIS key.
 *
 * Same request as {@see TestAiConnection} (both read the provider's model listing); they stay
 * separate tools because a client asking "does the key work?" and one asking "what can I pick?"
 * should not have to know they are the same question.
 */
final class ListAvailableModels implements Tool
{
    /** @var \Closure(): AiConfigurationStore */
    private readonly \Closure $store;

    /** @var \Closure(): AiConnectionTester */
    private readonly \Closure $tester;

    /**
     * @param \Closure(): AiConfigurationStore      $store  lazy — it resolves the shop DB
     * @param (\Closure(): AiConnectionTester)|null $tester
     */
    public function __construct(\Closure $store, ?\Closure $tester = null)
    {
        $this->store  = $store;
        $this->tester = $tester ?? static fn (): AiConnectionTester => new AiConnectionTester();
    }

    public function name(): string
    {
        return 'list_available_models';
    }

    public function description(): string
    {
        return 'Model ids the stored API key may use, asked live of the provider. Use it to pick '
            . 'a default model this account really has.';
    }

    /** @return array<string,mixed> */
    public function inputSchema(): array
    {
        return [
            'type'                 => 'object',
            // Empty array, not stdClass: the provider contract test asserts `properties` is an
            // array, and json_encode turns [] into [] rather than {} — which every MCP client
            // accepts for a no-argument tool.
            'properties'           => [],
            'additionalProperties' => false,
        ];
    }

    public function isWrite(): bool
    {
        return false;
    }

    public function scope(): string
    {
        return 'ai:read';
    }

    public function endpointClass(): EndpointClass
    {
        return EndpointClass::Reversible;
    }

    /**
     * @param array<string,mixed> $args
     *
     * @return array<string,mixed>
     */
    public function run(array $args): array
    {
        $store  = ($this->store)();
        $result = ($this->tester)()->testStored($store);

        if (!$result->ok) {
            return [
                'ok'     => false,
                'error'  => $result->message,
                'models' => [],
            ];
        }

        return [
            'ok'           => true,
            'models'       => $result->models,
            'count'        => \count($result->models),
            'currentModel' => $store->load()->model,
            'hint'         => $result->models === []
                ? 'This endpoint answered but exposes no model listing. Set the model id manually.'
                : '',
        ];
    }
}
