import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';

interface OpenRouterModel {
  id: string;
  name: string;
  context_length: number;
  architecture?: {
    input_modalities?: string[];
    output_modalities?: string[];
  };
  supported_parameters?: string[];
}

const OPENROUTER_MODELS_URL = 'https://openrouter.ai/api/v1/models';
const SYNC_BATCH_SIZE = 50;

function isTextChatModel(m: OpenRouterModel): boolean {
  const inputs = m.architecture?.input_modalities ?? [];
  const outputs = m.architecture?.output_modalities ?? [];
  return inputs.includes('text') && outputs.includes('text');
}

function supportsTools(m: OpenRouterModel): boolean {
  return (m.supported_parameters ?? []).includes('tools');
}

function cleanDisplayName(raw: string): string {
  return raw.length > 60 ? raw.slice(0, 57) + '...' : raw;
}

export interface SyncResult {
  fetched: number;
  upserted: number;
  skipped: number;
  error?: string;
}

export interface VerifyKeyResult {
  ok: boolean;
  error?: string;
  latencyMs?: number;
}

/**
 * Verifies the OpenRouter API key by issuing a cheap 1-token chat
 * completion against a fast free model. This exercises the same
 * `/v1/chat/completions` path used by the chat agent at runtime, so a
 * "verified" key is guaranteed to work end-to-end (a /key probe alone
 * does not catch billing / route-disabled situations). Returns
 * round-trip latency on success.
 */
export async function verifyKey(apiKey: string): Promise<VerifyKeyResult> {
  if (!apiKey || !apiKey.trim()) {
    return { ok: false, error: 'API key is empty' };
  }
  const start = Date.now();
  try {
    const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${apiKey}`,
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        // Use a free-tier model so verification doesn't require paid credits
        // on the caller's OpenRouter account; we only care that the
        // /v1/chat/completions path accepts the key end-to-end.
        model: 'meta-llama/llama-3.2-3b-instruct:free',
        max_tokens: 1,
        messages: [{ role: 'user', content: 'ping' }],
      }),
      signal: AbortSignal.timeout(15000),
    });
    const latencyMs = Date.now() - start;
    if (res.status === 401 || res.status === 403) {
      return { ok: false, error: 'OpenRouter rejected the API key (unauthorized).', latencyMs };
    }
    if (!res.ok) {
      const body = await res.text().catch(() => '');
      return { ok: false, error: `OpenRouter completion check failed: HTTP ${res.status}${body ? ` — ${body.slice(0, 200)}` : ''}`, latencyMs };
    }
    return { ok: true, latencyMs };
  } catch (err: unknown) {
    return { ok: false, error: `Key verification failed: ${err instanceof Error ? err.message : String(err)}`, latencyMs: Date.now() - start };
  }
}

/**
 * Convenience helper for routes: looks up the restaurant's stored
 * OpenRouter key (or env fallback) and verifies it.
 */
export async function verifyOpenRouterKey(restaurantId: string | null): Promise<VerifyKeyResult> {
  const { resolveApiKey } = await import('./provider-keys.service');
  const apiKey = restaurantId
    ? await resolveApiKey(restaurantId, 'openrouter')
    : process.env.OPENROUTER_API_KEY ?? null;
  if (!apiKey) {
    return { ok: false, error: 'No OpenRouter API key configured. Set one in Settings → API Keys to use OpenRouter models.' };
  }
  return verifyKey(apiKey);
}

export async function syncOpenRouterModels(apiKey?: string | null): Promise<SyncResult> {
  await initDatabase();

  /* raw: SELECT id FROM llm_providers WHERE name = 'openrouter' AND is_active = true */
  const { rows: providerRows } = await db.execute(sql`SELECT id FROM llm_providers WHERE name = 'openrouter' AND is_active = true`);
  const providerRow = providerRows[0] as { id: string } | undefined;
  if (!providerRow) {
    return { fetched: 0, upserted: 0, skipped: 0, error: 'OpenRouter provider not found' };
  }
  const providerId = providerRow.id;

  let models: OpenRouterModel[];
  try {
    const headers: Record<string, string> = { Accept: 'application/json' };
    if (apiKey && apiKey.trim()) headers.Authorization = `Bearer ${apiKey}`;
    const res = await fetch(OPENROUTER_MODELS_URL, {
      headers,
      signal: AbortSignal.timeout(20000),
    });
    if (!res.ok) {
      return { fetched: 0, upserted: 0, skipped: 0, error: `OpenRouter API error: ${res.status}` };
    }
    const data = await res.json() as { data?: OpenRouterModel[] };
    models = data.data ?? [];
  } catch (err: unknown) {
    return { fetched: 0, upserted: 0, skipped: 0, error: `Failed to fetch: ${err instanceof Error ? err.message : String(err)}` };
  }

  const chatModels = models.filter(isTextChatModel);

  let upserted = 0;
  let skipped = 0;

  for (let i = 0; i < chatModels.length; i += SYNC_BATCH_SIZE) {
    const batch = chatModels.slice(i, i + SYNC_BATCH_SIZE);
    for (const m of batch) {
      try {
        /* raw: INSERT INTO llm_models (...) VALUES (...) ON CONFLICT (...) DO UPDATE SET ... */
        await db.execute(sql`
          INSERT INTO llm_models (provider_id, model_id, display_name, context_length, supports_functions, is_active, display_order)
          VALUES (${providerId}, ${m.id}, ${cleanDisplayName(m.name)}, ${m.context_length ?? 0}, ${supportsTools(m)}, true, 100)
          ON CONFLICT (provider_id, model_id) DO UPDATE SET
            display_name = EXCLUDED.display_name,
            context_length = EXCLUDED.context_length,
            supports_functions = EXCLUDED.supports_functions,
            is_active = true
        `);
        upserted++;
      } catch {
        skipped++;
      }
    }
  }

  return { fetched: models.length, upserted, skipped };
}

export async function getOpenRouterModelCount(): Promise<number> {
  await initDatabase();
  /* raw: SELECT COUNT(*) as count FROM llm_models m JOIN llm_providers p ON p.id = m.provider_id WHERE p.name = 'openrouter' */
  const { rows } = await db.execute(sql`
    SELECT COUNT(*) as count FROM llm_models m
    JOIN llm_providers p ON p.id = m.provider_id
    WHERE p.name = 'openrouter'
  `);
  return parseInt((rows[0] as { count: string })?.count ?? '0', 10);
}

export async function listOpenRouterModels() {
  await initDatabase();
  /* raw: SELECT m.id, m.model_id, m.display_name, m.context_length, m.supports_functions FROM llm_models m JOIN llm_providers p ... */
  const { rows } = await db.execute(sql`
    SELECT m.id, m.model_id, m.display_name, m.context_length, m.supports_functions
    FROM llm_models m
    JOIN llm_providers p ON p.id = m.provider_id
    WHERE p.name = 'openrouter' AND m.is_active = true
    ORDER BY m.display_order, m.display_name
  `);
  return rows;
}
