/**
 * Regression tests for the router model name convention.
 *
 * Background — two rounds of this same shape of bug:
 *
 *   Round 1: Dynamic Routing silently no-op'd because ROUTER_MODEL
 *   was `'autopilot-model-router'` while the catalog keyed the entry
 *   as `'model-router'`. Fixed by changing the constant to the bare
 *   form.
 *
 *   Round 2: Even the bare `'model-router'` key didn't exist in the
 *   live catalog, because the catalog discovers deployments by
 *   underlying-model-name (not by Azure deployment-name). The team's
 *   actual deployment was named `autopilot-model-router` in Azure but
 *   its underlying model is `gpt-4.1-nano` — so the live catalog
 *   keys the entry under `gpt-4.1-nano`. The CLI now defaults to
 *   that name.
 *
 * These tests pin the new invariant + the override mechanism + catch
 * any reintroduction of a product-name prefix.
 *
 * Why a separate test file: the existing modelRouterService.test.js
 * mocks the constants module at the top of the file, so it cannot
 * assert anything about the REAL value of MODEL_ROUTER_CONFIG. This
 * file imports the real constants instead.
 */
import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SRC_ROOT  = path.resolve(__dirname, '../..');

describe('Router model naming — matches catalog convention', () => {
  test('default ROUTER_MODEL is "gpt-4.1-nano" (current live catalog key)', async () => {
    // The autopilot-model-router deployment's underlying model is
    // gpt-4.1-nano. The catalog keys entries by underlying model, so
    // this is the canonical name the CLI must request.
    delete process.env.LOXIA_ROUTER_MODEL;
    // Re-import to pick up the (re-)evaluated default.
    const fresh = await import(`../../utilities/constants.js?nocache=${Date.now()}`);
    expect(fresh.MODEL_ROUTER_CONFIG.ROUTER_MODEL).toBe('gpt-4.1-nano');
  });

  test('LOXIA_ROUTER_MODEL env var overrides the default (no rebuild needed)', async () => {
    process.env.LOXIA_ROUTER_MODEL = 'gpt-4o-mini';
    try {
      const fresh = await import(`../../utilities/constants.js?nocache=${Date.now()}`);
      expect(fresh.MODEL_ROUTER_CONFIG.ROUTER_MODEL).toBe('gpt-4o-mini');
    } finally {
      delete process.env.LOXIA_ROUTER_MODEL;
    }
  });

  test('ROUTER_MODEL does NOT carry a product/brand prefix', async () => {
    // Defense-in-depth: even if the canonical name ever changes,
    // it must not start with a product prefix like "autopilot-"
    // or "onbuzz-". The catalog's canonical names are product-agnostic.
    delete process.env.LOXIA_ROUTER_MODEL;
    const fresh = await import(`../../utilities/constants.js?nocache=${Date.now()}`);
    const v = fresh.MODEL_ROUTER_CONFIG.ROUTER_MODEL;
    expect(v).not.toMatch(/^autopilot[-_]/i);
    expect(v).not.toMatch(/^onbuzz[-_]/i);
    expect(v).not.toMatch(/^loxia[-_]/i);
  });
});

describe('REGRESSION: no stray "autopilot-model-router" literal in CLI source', () => {
  // Walks src/ and confirms the legacy prefixed string is gone. If
  // someone reintroduces it in a new file (a copy-paste from old
  // docs, a stale constant, an LLM-generated patch) this test catches
  // it before it ships. Comments and tests are excluded — they can
  // reference the legacy name for historical context.
  const STRAY = 'autopilot-model-router';
  const EXCLUDE = new Set(['__tests__', 'node_modules', 'dist', 'build', 'coverage', 'web-ui']);

  function walk(dir) {
    const out = [];
    for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
      if (EXCLUDE.has(entry.name) || entry.name.startsWith('.')) continue;
      const full = path.join(dir, entry.name);
      if (entry.isDirectory()) out.push(...walk(full));
      else if (entry.isFile() && /\.(js|cjs|mjs|ts)$/.test(entry.name)) out.push(full);
    }
    return out;
  }

  test('no .js/.ts file under src/ contains the legacy prefixed name (excluding tests + comments)', () => {
    const offenders = [];
    for (const file of walk(SRC_ROOT)) {
      const content = fs.readFileSync(file, 'utf8');
      // Strip single-line // comments and /* ... */ block comments so
      // documentation references don't trip the guard. Cheap regex —
      // close enough for a regression guard; not a JS parser.
      const stripped = content
        .replace(/\/\*[\s\S]*?\*\//g, '')
        .replace(/^\s*\/\/.*$/gm, '')
        .replace(/(['"`])[\s\S]*?\1/g, (m) => m.includes(STRAY) ? m : ''); // keep strings that contain it
      if (stripped.includes(STRAY)) {
        offenders.push(path.relative(SRC_ROOT, file));
      } else {
        // Also check raw content for STRAY in any string literal —
        // strip pass above keeps strings containing it for inspection.
        const inStringLiteral = /(['"`])[^'"`\n]*autopilot-model-router[^'"`\n]*\1/.test(content);
        if (inStringLiteral) offenders.push(path.relative(SRC_ROOT, file));
      }
    }
    expect(offenders).toEqual([]);
  });
});
