/**
 * Tests for query intent detection + adaptive RRF weights (TypeScript/Python parity).
 *
 * References:
 * - `ccw/src/tools/smart-search.ts` (detectQueryIntent, adjustWeightsByIntent, getRRFWeights)
 * - `codex-lens/src/codexlens/search/hybrid_search.py` (weight intent concept + defaults)
 */

import { describe, it, before } from 'node:test';
import assert from 'node:assert';

const smartSearchPath = new URL('../dist/tools/smart-search.js', import.meta.url).href;

describe('Smart Search - Query Intent + RRF Weights', async () => {
  /** @type {any} */
  let smartSearchModule;

  before(async () => {
    try {
      smartSearchModule = await import(smartSearchPath);
    } catch (err) {
      // Keep tests non-blocking for environments that haven't built `ccw/dist` yet.
      console.log('Note: smart-search module import skipped:', err.message);
    }
  });

  describe('detectQueryIntent', () => {
    it('classifies "def authenticate" as keyword', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('def authenticate'), 'keyword');
    });

    it('classifies CamelCase identifiers as keyword', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('MyClass'), 'keyword');
    });

    it('classifies snake_case identifiers as keyword', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('user_id'), 'keyword');
    });

    it('classifies namespace separators "::" as keyword', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('UserService::authenticate'), 'keyword');
    });

    it('classifies pointer arrows "->" as keyword', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('ptr->next'), 'keyword');
    });

    it('classifies dotted member access as keyword', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('foo.bar'), 'keyword');
    });

    it('classifies natural language questions as semantic', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('how to handle user login'), 'semantic');
    });

    it('classifies interrogatives with question marks as semantic', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('what is authentication?'), 'semantic');
    });

    it('classifies queries with both code + NL signals as mixed', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('why does FooBar crash?'), 'mixed');
    });

    it('classifies long NL queries containing identifiers as mixed', () => {
      if (!smartSearchModule) return;
      assert.strictEqual(smartSearchModule.detectQueryIntent('how to use user_id in query'), 'mixed');
    });
  });

  describe('classifyIntent lexical routing', () => {
    it('routes config/backend queries to exact when index and embeddings are available', () => {
      if (!smartSearchModule) return;
      const classification = smartSearchModule.__testables.classifyIntent(
        'embedding backend fastembed local litellm api config',
        true,
        true,
      );
      assert.strictEqual(classification.mode, 'exact');
      assert.match(classification.reasoning, /lexical priority/i);
    });

    it('routes generated artifact queries to exact when index and embeddings are available', () => {
      if (!smartSearchModule) return;
      const classification = smartSearchModule.__testables.classifyIntent('dist bundle output', true, true);
      assert.strictEqual(classification.mode, 'exact');
      assert.match(classification.reasoning, /generated artifact/i);
    });
  });

  describe('adjustWeightsByIntent', () => {
    it('maps keyword intent to exact-heavy weights', () => {
      if (!smartSearchModule) return;
      const weights = smartSearchModule.adjustWeightsByIntent('keyword', { exact: 0.3, fuzzy: 0.1, vector: 0.6 });
      assert.deepStrictEqual(weights, { exact: 0.5, fuzzy: 0.1, vector: 0.4 });
    });
  });

  describe('getRRFWeights parity set', () => {
    it('produces stable weights for 20 representative queries', () => {
      if (!smartSearchModule) return;

      const base = { exact: 0.3, fuzzy: 0.1, vector: 0.6 };
      const expected = [
        ['def authenticate', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['class UserService', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['user_id', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['MyClass', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['Foo::Bar', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['ptr->next', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['foo.bar', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['import os', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['how to handle user login', { exact: 0.2, fuzzy: 0.1, vector: 0.7 }],
        ['what is the best way to search?', { exact: 0.2, fuzzy: 0.1, vector: 0.7 }],
        ['explain the authentication flow', { exact: 0.2, fuzzy: 0.1, vector: 0.7 }],
        ['generate embeddings for this repo', { exact: 0.2, fuzzy: 0.1, vector: 0.7 }],
        ['how does FooBar work', base],
        ['user_id how to handle', base],
        ['Find UserService::authenticate method', base],
        ['where is foo.bar used', base],
        ['parse_json function', { exact: 0.5, fuzzy: 0.1, vector: 0.4 }],
        ['How to parse_json output?', base],
        ['', base],
        ['authentication', base],
      ];

      for (const [query, expectedWeights] of expected) {
        const actual = smartSearchModule.getRRFWeights(query, base);
        assert.deepStrictEqual(actual, expectedWeights, `unexpected weights for query: ${JSON.stringify(query)}`);
      }
    });
  });
});
