// Copyright (c) 2026 defconxt. All rights reserved.
// Licensed under AGPL-3.0 — see LICENSE file for details.

/**
 * NERF Benchmark — LLM client factory for the security agent.
 *
 * All LLM access goes through pi-mono's pi-ai unified API.
 * Returns an Anthropic-SDK-compatible client for tool-use.
 *
 * Provider discovery order (when no backendOverride specified):
 *   1. Anthropic (if ANTHROPIC_API_KEY set)
 *   2. Ollama at localhost:11434 (if alive)
 *   3. Error with guidance
 */

import { importPiAi } from '../gateway/pi-imports.js';
import { makePiClient } from '../gateway/client-pi.js';

// ---------------------------------------------------------------------------
// Client factory
// ---------------------------------------------------------------------------

/**
 * Create an LLM client for the benchmark agent.
 * Always returns an Anthropic-SDK-compatible interface.
 *
 * @param {object} [opts]
 * @param {string} [opts.backendOverride] - Force a specific backend
 * @param {string} [opts.provider] - pi-ai provider name
 * @param {string} [opts.model] - Model identifier
 * @param {object} [opts.piOptions] - Provider-specific options
 * @returns {Promise<{ client: object, model: string }>}
 */
export async function makeAgentClient(opts = {}) {
  const backendOverride = opts.backendOverride;

  // Explicit pi-ai backend
  if (backendOverride === 'pi-ai' || backendOverride === 'pi') {
    return makePiClient({
      provider: opts.provider || 'anthropic',
      model: opts.model || 'claude-sonnet-4-6',
      options: opts.piOptions || undefined,
    });
  }

  // Explicit Anthropic
  if (backendOverride === 'claude' || backendOverride === 'anthropic') {
    return makePiClient({
      provider: 'anthropic',
      model: opts.model || 'claude-sonnet-4-6',
    });
  }

  // Explicit Ollama
  if (backendOverride === 'ollama') {
    const ollamaModel = opts.model || await _pickOllamaModel() || 'qwen2.5:32b';
    return makePiClient({
      provider: 'openai-completions',
      model: ollamaModel,
      options: { baseUrl: 'http://127.0.0.1:11434/v1', apiKey: 'ollama' },
    });
  }

  // Auto-discovery: try Anthropic first, then Ollama
  if (!backendOverride) {
    // Check for Anthropic API key
    const piAi = await importPiAi();
    const anthropicKey = piAi.getEnvApiKey?.('anthropic');
    if (anthropicKey || process.env.ANTHROPIC_API_KEY) {
      return makePiClient({
        provider: 'anthropic',
        model: opts.model || 'claude-sonnet-4-6',
      });
    }

    // Try Ollama
    const ollamaModel = await _pickOllamaModel();
    if (ollamaModel) {
      return makePiClient({
        provider: 'openai-completions',
        model: ollamaModel,
        options: { baseUrl: 'http://127.0.0.1:11434/v1', apiKey: 'ollama' },
      });
    }
  }

  throw new Error(
    'No LLM backend available.\n' +
    'Options:\n' +
    '  1. Set ANTHROPIC_API_KEY environment variable\n' +
    '  2. Start Ollama at localhost:11434\n' +
    '  3. Pass --backend pi-ai --provider <name> --model <model>',
  );
}

// ---------------------------------------------------------------------------
// Ollama model discovery
// ---------------------------------------------------------------------------

const PREFERRED_OLLAMA_MODELS = [
  'qwen2.5:32b', 'nerf:latest', 'qwen2.5-coder:14b',
  'qwen2.5:14b', 'llama3.1:70b',
];

/**
 * Probe Ollama at localhost:11434 and pick the best available model.
 * Returns null if Ollama isn't running.
 */
async function _pickOllamaModel() {
  try {
    const { request } = await import('node:http');
    const data = await new Promise((resolve) => {
      const req = request(
        { hostname: '127.0.0.1', port: 11434, path: '/api/tags', timeout: 2000 },
        (res) => {
          let body = '';
          res.on('data', (d) => body += d);
          res.on('end', () => {
            try { resolve(JSON.parse(body)); }
            catch { resolve(null); }
          });
        },
      );
      req.on('error', () => resolve(null));
      req.on('timeout', () => { req.destroy(); resolve(null); });
      req.end();
    });

    if (!data?.models) return null;

    const available = data.models.map(m => m.name);
    for (const preferred of PREFERRED_OLLAMA_MODELS) {
      if (available.includes(preferred)) return preferred;
    }
    return available[0] || null;
  } catch {
    return null;
  }
}
