<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\Admin;

use JTL\DB\DbInterface;
use Throwable;

/**
 * Read-only view model for the admin "Integrations" tab: a fixed catalog of AI providers, each with
 * a short connect guide and a live status dot derived from REAL signal — never a hardcoded state.
 *
 * Status per provider:
 *   green  — the provider has at least one live (non-revoked, unexpired) OAuth access token, i.e. it
 *            is currently connected.
 *   orange — a supported provider with no live token: available but not (yet) set up, or a previous
 *            token has expired/been revoked.
 *   red    — the provider does not support remote MCP connectors today (nothing to connect).
 *
 * Providers are matched to registered {@see tnt_mcp_oauth_client} rows by the OAuth client name and
 * by the host of its redirect URI(s), so a client that self-identifies as "ChatGPT" (or redirects to
 * chatgpt.com) counts toward the ChatGPT dot. Pure aggregation over the injected DB — unit-testable
 * with a mock — and every read is best-effort: a missing table degrades to "no live token".
 */
final class IntegrationsReport
{
    /**
     * @param ?string $language {@see AdminLanguage::DE} or {@see AdminLanguage::EN}; `null` resolves
     *                          the admin session's language (German off-session).
     */
    public function __construct(
        private readonly DbInterface $db,
        private readonly ?string $language = null,
    ) {
    }

    /**
     * @param non-empty-string $baseUrl absolute shop base URL, used to build the MCP endpoint URL
     * @return list<array{
     *     id:string,name:string,supported:bool,status:string,statusLabel:string,
     *     connectUrl:string,steps:list<string>,note:string
     * }>
     */
    public function build(string $baseUrl): array
    {
        $endpoint  = rtrim($baseUrl, '/') . '/tnt-mcp';
        $liveHosts = $this->liveConnectedProviders();

        $out = [];
        foreach (self::catalog($endpoint) as $p) {
            $connected = false;
            foreach ($p['keys'] as $key) {
                if (isset($liveHosts[$key])) {
                    $connected = true;
                    break;
                }
            }

            if (!$p['supported']) {
                $status = 'red';
            } elseif ($connected) {
                $status = 'green';
            } else {
                $status = 'orange';
            }

            $out[] = [
                'id'          => $p['id'],
                'name'        => $p['name'],
                'supported'   => $p['supported'],
                'status'      => $status,
                'statusLabel' => $this->pick(self::statusLabels()[$status]),
                'connectUrl'  => $p['supported'] ? $endpoint : '',
                'steps'       => \array_map($this->pick(...), $p['steps']),
                'note'        => $this->pick($p['note']),
            ];
        }

        return $out;
    }

    /**
     * Set of provider match-keys (lower-case keyword tokens) that currently have at least one live
     * OAuth access token, derived by joining live tokens to their client's name + redirect hosts.
     *
     * @return array<string,true>
     */
    private function liveConnectedProviders(): array
    {
        try {
            $rows = $this->db->getObjects(
                'SELECT c.cName AS name, c.cRedirectUris AS redirects
                   FROM tnt_mcp_oauth_client c
                   JOIN tnt_mcp_oauth_access_token t ON t.cClientId = c.cClientId
                  WHERE t.nRevoked = 0 AND t.dExpires > NOW()
                  GROUP BY c.cClientId, c.cName, c.cRedirectUris',
            );
        } catch (Throwable) {
            return [];
        }

        $keys = [];
        foreach ($rows as $row) {
            $haystack = \strtolower((string)($row->name ?? '') . ' ' . (string)($row->redirects ?? ''));
            foreach (self::catalog('')  as $p) {
                foreach ($p['keys'] as $key) {
                    if ($key !== '' && \str_contains($haystack, $key)) {
                        $keys[$key] = true;
                    }
                }
            }
        }

        return $keys;
    }

    /**
     * The three dot states, labelled. Public so a test can assert every state this class can emit
     * carries both languages.
     *
     * @return array<string, array{de: string, en: string}>
     */
    public static function statusLabels(): array
    {
        return [
            'green'  => ['de' => 'Verbunden', 'en' => 'Connected'],
            'orange' => [
                'de' => 'Verfügbar – noch nicht verbunden',
                'en' => 'Available – not connected yet',
            ],
            'red'    => ['de' => 'Nicht unterstützt', 'en' => 'Not supported'],
        ];
    }

    /** @param array{de: string, en: string} $texts */
    private function pick(array $texts): string
    {
        return AdminLanguage::pick($texts, $this->language);
    }

    /**
     * Fixed provider catalog. `keys` are the lower-case tokens matched against a client's name and
     * redirect-URI host to attribute it to this provider. Prose travels as a `de`/`en` pair — the
     * connect guides are the one place a non-German-speaking operator has to follow instructions.
     *
     * @return list<array{
     *     id:string, name:string, supported:bool, keys:list<string>,
     *     steps:list<array{de: string, en: string}>, note:array{de: string, en: string}
     * }>
     */
    private static function catalog(string $endpoint): array
    {
        return [
            [
                'id'        => 'chatgpt',
                'name'      => 'ChatGPT',
                'supported' => true,
                'keys'      => ['chatgpt', 'openai'],
                'note'      => [
                    'de' => 'Erfordert ChatGPT mit Connector-/Entwicklermodus '
                        . '(Plus, Pro, Team oder Enterprise).',
                    'en' => 'Requires ChatGPT with connector/developer mode '
                        . '(Plus, Pro, Team or Enterprise).',
                ],
                'steps'     => [
                    [
                        'de' => 'In ChatGPT: Einstellungen → Connectors → „Erstellen“ '
                            . '(bzw. Entwicklermodus aktivieren).',
                        'en' => 'In ChatGPT: Settings → Connectors → "Create" '
                            . '(or enable developer mode).',
                    ],
                    [
                        'de' => 'MCP-Server-URL einfügen: ' . $endpoint,
                        'en' => 'Paste the MCP server URL: ' . $endpoint,
                    ],
                    [
                        'de' => 'Als Authentifizierung „OAuth“ wählen und auf „Verbinden“ klicken.',
                        'en' => 'Choose "OAuth" as the authentication and click "Connect".',
                    ],
                    [
                        'de' => 'Im geöffneten Fenster als JTL-Backend-Administrator anmelden und '
                            . 'den Zugriff bestätigen.',
                        'en' => 'In the window that opens, sign in as a JTL backend administrator '
                            . 'and confirm the access.',
                    ],
                ],
            ],
            [
                'id'        => 'claude',
                'name'      => 'Claude',
                'supported' => true,
                'keys'      => ['claude', 'anthropic'],
                'note'      => [
                    'de' => 'Remote-MCP-Connectoren sind in Claude (Pro/Team/Enterprise) verfügbar.',
                    'en' => 'Remote MCP connectors are available in Claude (Pro/Team/Enterprise).',
                ],
                'steps'     => [
                    [
                        'de' => 'In Claude: Einstellungen → Connectors → „Custom Connector hinzufügen“.',
                        'en' => 'In Claude: Settings → Connectors → "Add custom connector".',
                    ],
                    [
                        'de' => 'MCP-Server-URL einfügen: ' . $endpoint,
                        'en' => 'Paste the MCP server URL: ' . $endpoint,
                    ],
                    [
                        'de' => 'Auf „Verbinden“ klicken.',
                        'en' => 'Click "Connect".',
                    ],
                    [
                        'de' => 'Als JTL-Backend-Administrator anmelden und den Zugriff bestätigen.',
                        'en' => 'Sign in as a JTL backend administrator and confirm the access.',
                    ],
                ],
            ],
            [
                'id'        => 'antigravity',
                'name'      => 'Antigravity (Google)',
                'supported' => true,
                'keys'      => ['antigravity', 'agy'],
                'note'      => [
                    'de' => 'Google Antigravity (AGY) – Agentisches KI-Coding. '
                        . 'Benötigt die Antigravity CLI.',
                    'en' => 'Google Antigravity (AGY) – agentic AI coding. '
                        . 'Requires the Antigravity CLI.',
                ],
                'steps'     => [
                    [
                        'de' => 'Im Terminal ausführen: curl -sL '
                            . $endpoint . '/agy-config > ~/.gemini/config/mcp_config.json',
                        'en' => 'Run in your terminal: curl -sL '
                            . $endpoint . '/agy-config > ~/.gemini/config/mcp_config.json',
                    ],
                    [
                        'de' => 'Antigravity starten — der Assistent entdeckt den MCP-Server '
                            . 'automatisch und öffnet die OAuth-Anmeldung im Browser.',
                        'en' => 'Start Antigravity — the assistant discovers the MCP server '
                            . 'automatically and opens the OAuth login in your browser.',
                    ],
                    [
                        'de' => 'Als JTL-Backend-Administrator anmelden und den Zugriff bestätigen.',
                        'en' => 'Sign in as a JTL backend administrator and confirm the access.',
                    ],
                ],
            ],
            [
                'id'        => 'gemini',
                'name'      => 'Google Gemini',
                'supported' => false,
                'keys'      => ['gemini'],
                'note'      => [
                    'de' => 'Google Gemini unterstützt derzeit keine Remote-MCP-Connectoren. '
                        . 'Sobald verfügbar, kann dieselbe Server-URL genutzt werden.',
                    'en' => 'Google Gemini does not support remote MCP connectors today. '
                        . 'Once it does, the same server URL can be used.',
                ],
                'steps'     => [],
            ],
            [
                'id'        => 'copilot',
                'name'      => 'Microsoft Copilot',
                'supported' => false,
                'keys'      => ['copilot'],
                'note'      => [
                    'de' => 'Microsoft Copilot unterstützt derzeit keine Remote-MCP-Connectoren. '
                        . 'Sobald verfügbar, kann dieselbe Server-URL genutzt werden.',
                    'en' => 'Microsoft Copilot does not support remote MCP connectors today. '
                        . 'Once it does, the same server URL can be used.',
                ],
                'steps'     => [],
            ],
        ];
    }
}
