import type { FastifyInstance } from 'fastify';
import type { Config } from '../config.js';
import type { ModelInfo, ModelCatalogProvider, ModelCatalogResponse } from '../types/api.js';
import { getLlamaProviders } from '../services/llama-providers.js';

interface LlamaSwapModelsResponse {
  data?: ModelInfo[];
}

const DEEPSEEK_STATIC_MODELS: ModelInfo[] = [
  { id: 'deepseek-v4-flash', object: 'model', created: 0, owned_by: 'deepseek' },
  { id: 'deepseek-v4-pro', object: 'model', created: 0, owned_by: 'deepseek' },
];

// Anthropic's /v1/models needs different headers (x-api-key + anthropic-version)
// and a different response shape, so we surface a curated static list instead.
const ANTHROPIC_STATIC_MODELS: ModelInfo[] = [
  { id: 'claude-opus-4-8', object: 'model', created: 0, owned_by: 'anthropic' },
  { id: 'claude-opus-4-7', object: 'model', created: 0, owned_by: 'anthropic' },
  { id: 'claude-sonnet-4-6', object: 'model', created: 0, owned_by: 'anthropic' },
  { id: 'claude-haiku-4-5', object: 'model', created: 0, owned_by: 'anthropic' },
];

export function registerModelRoutes(app: FastifyInstance, config: Config): void {
  app.get('/api/models', async (_req, reply) => {
    const providers: ModelCatalogProvider[] = [];

    // 1. Fetch live model lists from each configured local provider.
    const registry = getLlamaProviders();
    for (const provider of registry.providers) {
      const models: ModelInfo[] = [];
      try {
        const res = await fetch(`${provider.baseUrl}/v1/models`);
        if (res.ok) {
          const parsed = (await res.json()) as LlamaSwapModelsResponse;
          if (parsed.data) {
            // Prefix every model id with "provider/" to make it composite (D-2).
            for (const m of parsed.data) {
              models.push({ ...m, id: `${provider.id}/${m.id}` });
            }
          }
        }
      } catch {
        // Provider unreachable — include empty entry so the UI can show it.
      }
      providers.push({ id: provider.id, label: provider.label, models });
    }

    // 2. If DeepSeek is configured, add a synthetic "deepseek" provider group.
    if (config.DEEPSEEK_API_KEY) {
      const deepseekModels: ModelInfo[] = [];
      try {
        const baseURL = (config.DEEPSEEK_BASE_URL ?? 'https://api.deepseek.com').replace(/\/+$/, '');
        const res = await fetch(`${baseURL}/v1/models`, {
          headers: { Authorization: `Bearer ${config.DEEPSEEK_API_KEY}` },
          signal: AbortSignal.timeout(5_000),
        });
        if (res.ok) {
          const parsed = (await res.json()) as LlamaSwapModelsResponse;
          if (parsed.data) {
            for (const m of parsed.data) {
              deepseekModels.push({ ...m, id: `deepseek/${m.id}` });
            }
          }
        } else {
          deepseekModels.push(...DEEPSEEK_STATIC_MODELS.map((m) => ({ ...m, id: `deepseek/${m.id}` })));
        }
      } catch {
        deepseekModels.push(...DEEPSEEK_STATIC_MODELS.map((m) => ({ ...m, id: `deepseek/${m.id}` })));
      }
      providers.push({ id: 'deepseek', label: 'DeepSeek', models: deepseekModels });
    }

    // 3. If Anthropic is configured, add a synthetic "anthropic" provider group.
    if (config.ANTHROPIC_API_KEY) {
      const anthropicModels = ANTHROPIC_STATIC_MODELS.map((m) => ({ ...m, id: `anthropic/${m.id}` }));
      providers.push({ id: 'anthropic', label: 'Anthropic', models: anthropicModels });
    }

    if (providers.length === 0) {
      reply.code(502);
      return { error: 'no models available from any provider' };
    }
    return { providers } satisfies ModelCatalogResponse;
  });
}
