/**
 * Registry tests — lock the contract every UI / config / agent path
 * leans on:
 *   1. listGroundingModels() returns objects with the shape the form
 *      generator expects (id, displayName, knobs[], capabilities)
 *   2. createGroundingModel() resolves the registered id and rejects
 *      unknown ids with a typed error
 *   3. The MOCK adapter is hidden by default (so it never accidentally
 *      surfaces in the production UI dropdown)
 */

import { describe, test, expect } from '@jest/globals';
import {
  createGroundingModel,
  listGroundingModels,
  isRegistered,
  listModelIds,
} from '../registry.js';
import { ModelId, ErrorCode } from '../constants.js';
import KimiAdapter from '../KimiAdapter.js';
import MockAdapter from '../MockAdapter.js';

describe('listGroundingModels', () => {
  test('returns Kimi by default; hides MOCK', () => {
    const out = listGroundingModels();
    const ids = out.map(m => m.id);
    expect(ids).toContain(ModelId.KIMI_K2_6);
    expect(ids).not.toContain(ModelId.MOCK);
  });

  test('includeInternal=true reveals MOCK (for tests / dev tools)', () => {
    const out = listGroundingModels({ includeInternal: true });
    expect(out.map(m => m.id)).toContain(ModelId.MOCK);
  });

  test('each entry has the shape the admin form-generator needs', () => {
    for (const m of listGroundingModels({ includeInternal: true })) {
      expect(typeof m.id).toBe('string');
      expect(typeof m.displayName).toBe('string');
      expect(m.displayName.length).toBeGreaterThan(0);
      expect(Array.isArray(m.knobs)).toBe(true);
      expect(typeof m.capabilities).toBe('object');
    }
  });

  test('Kimi entry exposes the expected knob set (locks the UI contract)', () => {
    const kimi = listGroundingModels().find(m => m.id === ModelId.KIMI_K2_6);
    expect(kimi).toBeTruthy();
    const knobIds = kimi.knobs.map(k => k.id).sort();
    expect(knobIds).toContain('reasoning_effort');
    expect(knobIds).toContain('temperature');
    expect(knobIds).toContain('top_p');
    expect(knobIds).toContain('image_detail');
  });
});

describe('createGroundingModel', () => {
  test('creates a KimiAdapter instance for the Kimi id', () => {
    const m = createGroundingModel(ModelId.KIMI_K2_6, {
      endpoint: 'https://e/', apiKey: 'k',
    });
    expect(m).toBeInstanceOf(KimiAdapter);
  });

  test('creates a MockAdapter for the mock id', () => {
    const m = createGroundingModel(ModelId.MOCK, {});
    expect(m).toBeInstanceOf(MockAdapter);
  });

  test('throws typed error for unknown id', () => {
    expect.assertions(2);
    try {
      createGroundingModel('imaginary-model', {});
    } catch (err) {
      expect(err.code).toBe(ErrorCode.UNKNOWN_MODEL);
      expect(err.message).toMatch(/imaginary-model/);
    }
  });
});

describe('isRegistered + listModelIds', () => {
  test('isRegistered reflects the actual map', () => {
    expect(isRegistered(ModelId.KIMI_K2_6)).toBe(true);
    expect(isRegistered(ModelId.MOCK)).toBe(true);
    expect(isRegistered('not-here')).toBe(false);
  });

  test('listModelIds includes internal entries (unfiltered)', () => {
    const ids = listModelIds();
    expect(ids).toContain(ModelId.KIMI_K2_6);
    expect(ids).toContain(ModelId.MOCK);
  });
});

// ─── REGRESSION: catalog-name contract ─────────────────────────────────
// The backend's POST /llm/grounding does a CASE-SENSITIVE lookup of the
// `model` field against the Loxia model catalog (autopilot-model-
// catalog). If the CLI sends a name that doesn't match the catalog
// exactly, the catalog lookup falls through to "no deployment found",
// Foundry gets a bogus deployment name, and the response is:
//   "Backend 404: API deployment for this resource does not exist"
// (observed in the OS Agent log on 2026-05-13 — every intent-click
// failed because the catalog has "Kimi-K2.6" while the CLI was sending
// "kimi-k2.6").
//
// This test pins the constant to the catalog's canonical spelling. If
// someone tries to "normalise" it to lowercase or rename the model
// without a coordinated catalog change, this test fails BEFORE shipping
// instead of in production.
describe('REGRESSION: catalog-name contract for KIMI_K2_6', () => {
  test('ModelId.KIMI_K2_6 matches the catalog spelling "Kimi-K2.6" exactly', () => {
    expect(ModelId.KIMI_K2_6).toBe('Kimi-K2.6');
  });

  test('the value is a non-empty PascalCase string with the K2.6 version', () => {
    expect(typeof ModelId.KIMI_K2_6).toBe('string');
    expect(ModelId.KIMI_K2_6.length).toBeGreaterThan(0);
    // Catalog uses PascalCase; if this ever changes the proxy lookup
    // will return 404. Lock the shape explicitly.
    expect(ModelId.KIMI_K2_6).toMatch(/^Kimi-K2\.6$/);
  });
});
