/**
 * Model Discovery Service
 *
 * Fetches available models from MODELS_API_BASE_URL/v1/models (9router or
 * any OpenAI-compatible endpoint). Falls back to hardcoded constants when
 * the endpoint is unavailable or not configured.
 *
 * Cache TTL: 5 minutes. Re-fetched on first request after expiry.
 */

import { CLAUDE_MODELS } from '../../shared/modelConstants.js';

const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes

let cachedModels = null;
let cacheExpiry = 0;

/**
 * Maps a raw /v1/models entry to the { value, label } shape used by the UI.
 * Filters to models that are relevant for Claude routing (id contains 'claude'
 * or starts with 'cc/').
 *
 * @param {Object} entry - Raw model object from /v1/models
 * @returns {{ value: string, label: string } | null}
 */
function mapModelEntry(entry) {
  const id = typeof entry?.id === 'string' ? entry.id.trim() : null;
  if (!id) return null;

  // Only surface Claude-family and cc/* (9router) models for the Claude provider.
  // Extend this filter if you want to surface all models.
  const isClaude = id.toLowerCase().includes('claude') || id.startsWith('cc/');
  if (!isClaude) return null;

  // Build a human-readable label from the id.
  // e.g. "cc/claude-sonnet-4-6" → "claude-sonnet-4-6 (9router)"
  //      "claude-3-5-sonnet-20241022" → "claude-3-5-sonnet-20241022"
  let label = entry.name ?? id;
  if (id.startsWith('cc/')) {
    label = `${id.slice(3)} (9router)`;
  }

  return { value: id, label };
}

/**
 * Fetches models from the configured MODELS_API_BASE_URL/v1/models endpoint.
 * Returns null on failure (caller uses fallback).
 *
 * @returns {Promise<Array<{value: string, label: string}> | null>}
 */
async function fetchModelsFromApi() {
  const baseUrl = process.env.MODELS_API_BASE_URL || process.env.ANTHROPIC_BASE_URL;
  if (!baseUrl) return null;

  // Strip trailing /v1 if present so we can append /v1/models cleanly.
  const cleanBase = baseUrl.replace(/\/v1\/?$/, '');
  const url = `${cleanBase}/v1/models`;

  try {
    const apiKey = process.env.ANTHROPIC_API_KEY || '';
    const res = await fetch(url, {
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      signal: AbortSignal.timeout(8000),
    });

    if (!res.ok) {
      console.warn(`[model-discovery] GET ${url} returned ${res.status}`);
      return null;
    }

    const json = await res.json();
    // OpenAI-compatible: { data: [...] }  or  { models: [...] }  or  plain array
    const raw = Array.isArray(json) ? json : (json.data ?? json.models ?? []);
    if (!Array.isArray(raw)) return null;

    const mapped = raw.map(mapModelEntry).filter(Boolean);
    if (mapped.length === 0) return null;

    console.log(`[model-discovery] Loaded ${mapped.length} Claude models from ${url}`);
    return mapped;
  } catch (err) {
    console.warn(`[model-discovery] Failed to fetch ${url}:`, err?.message ?? err);
    return null;
  }
}

/**
 * Returns the list of available Claude model options.
 * Uses cached results when fresh; falls back to CLAUDE_MODELS.OPTIONS.
 *
 * @returns {Promise<Array<{value: string, label: string}>>}
 */
export async function getAvailableClaudeModels() {
  const now = Date.now();
  if (cachedModels && now < cacheExpiry) {
    return cachedModels;
  }

  const discovered = await fetchModelsFromApi();
  if (discovered && discovered.length > 0) {
    cachedModels = discovered;
    cacheExpiry = now + CACHE_TTL_MS;
    return cachedModels;
  }

  // Fallback: hardcoded list from shared constants
  return CLAUDE_MODELS.OPTIONS;
}

/**
 * Invalidates the model cache, forcing re-fetch on next call.
 */
export function invalidateModelCache() {
  cachedModels = null;
  cacheExpiry = 0;
}
