<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Enums;

/**
 * Which LLM vendor the shop's AI actions talk to (epic D2 — "provider fully swappable").
 *
 * The value is what `tnt_mcp_ai_config.provider` stores AND what the MCP tools accept, so there is
 * one spelling everywhere.
 *
 * `COMPATIBLE` is the escape hatch and the reason this enum stays this short: every self-hosted
 * runtime worth naming (vLLM, Ollama, LM Studio, OpenRouter, Azure's OpenAI deployments) speaks the
 * OpenAI wire format, so they are one case with a merchant-supplied base URL rather than five cases
 * that would each need their own release. Adding a case is for a genuinely different WIRE FORMAT —
 * which is exactly what separates {@see self::ANTHROPIC} from {@see self::OPENAI}: a different auth
 * header, a different models path, and a required version header.
 */
enum AiProviders: string
{
    case OPENAI = 'openai';

    case ANTHROPIC = 'anthropic';

    /** Any OpenAI-wire-format endpoint at a merchant-supplied base URL. */
    case COMPATIBLE = 'compatible';

    /**
     * @return list<string>
     */
    public static function values(): array
    {
        return \array_map(static fn (self $case): string => $case->value, self::cases());
    }

    /** What the merchant sees; also the `allowedValues` label an MCP client is shown. */
    public function label(): string
    {
        return match ($this) {
            self::OPENAI     => 'OpenAI',
            self::ANTHROPIC  => 'Anthropic (Claude)',
            self::COMPATIBLE => 'OpenAI-compatible endpoint (own base URL)',
        };
    }

    /**
     * The default API root, without a trailing slash. `COMPATIBLE` has none by definition — its
     * whole point is that the merchant supplies one — and callers must treat '' as "required".
     */
    public function defaultBaseUrl(): string
    {
        return match ($this) {
            self::OPENAI     => 'https://api.openai.com/v1',
            self::ANTHROPIC  => 'https://api.anthropic.com/v1',
            self::COMPATIBLE => '',
        };
    }

    /**
     * Path under the base URL that lists the models the key may use. Both wire formats expose one,
     * which is why {@see \Plugin\tnt_mcp\src\Ai\AiConnectionTester} can use a single request
     * to answer both "is this key valid?" and "which models are there?".
     */
    public function modelsPath(): string
    {
        return '/models';
    }

    /**
     * Path under the base URL that produces a completion (epic 4.4).
     *
     * The two wire formats differ in more than this path — the request body and the response shape
     * are different too, which is what {@see \Plugin\tnt_mcp\src\Ai\AiChatClient} exists to
     * absorb. The path is here because it is provider KNOWLEDGE, and this enum is where all of it
     * lives; a second place to look would be the start of the drift this design avoids.
     */
    public function completionsPath(): string
    {
        return match ($this) {
            self::ANTHROPIC                => '/messages',
            self::OPENAI, self::COMPATIBLE => '/chat/completions',
        };
    }

    /**
     * How the key is presented. Anthropic does NOT accept `Authorization: Bearer` and OpenAI does
     * not read `x-api-key`, so this is not cosmetic — it is the difference between a working key
     * and a 401 the merchant cannot explain.
     *
     * @return array<string,string> header name => value, with the key already substituted
     */
    public function authHeaders(string $apiKey): array
    {
        return match ($this) {
            self::ANTHROPIC => [
                'x-api-key'         => $apiKey,
                'anthropic-version' => '2023-06-01',
            ],
            self::OPENAI, self::COMPATIBLE => [
                'Authorization' => 'Bearer ' . $apiKey,
            ],
        };
    }
}
