'use strict';

const fs = require('fs');
const path = require('path');

function safeJson(value, fallback) {
  if (!value) return fallback;
  if (typeof value !== 'string') return value;
  try { return JSON.parse(value); } catch { return fallback; }
}

function displayNameForProviderType(type) {
  switch (String(type || '').toLowerCase()) {
    case 'anthropic': return 'Anthropic';
    case 'openai': return 'OpenAI';
    case 'google': return 'Google Gemini';
    case 'deepseek': return 'DeepSeek';
    case 'moonshot': return 'Moonshot / Kimi';
    case 'ollama': return 'Ollama';
    default:
      return String(type || 'Provider')
        .split(/[-_]+/)
        .filter(Boolean)
        .map((part) => part.length <= 3 ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1))
        .join(' ') || 'Provider';
  }
}

function resolveBrainDbPath(env = process.env) {
  if (env.WALL_E_BRAIN_DB) return env.WALL_E_BRAIN_DB;
  const dataDir = env.WALL_E_DATA_DIR || env.WALLE_DATA_DIR || path.join(env.HOME || '', '.walle', 'data');
  return dataDir ? path.join(dataDir, 'wall-e-brain.db') : '';
}

function tableExists(db, tableName) {
  try {
    return !!db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(tableName);
  } catch {
    return false;
  }
}

function kvMap(db) {
  if (!tableExists(db, 'kv_store')) return new Map();
  try {
    return new Map(db.prepare(`
      SELECT key, value
      FROM kv_store
      WHERE key IN (
        'model_gateway_sync:portkey:last_success_at',
        'model_gateway_sync:portkey:last_run_at',
        'model_gateway_sync:portkey:last_error',
        'shadow_training_enabled'
      )
      OR key LIKE 'model_provider_route_policy:%'
    `).all().map((row) => [String(row.key || ''), row.value]));
  } catch {
    return new Map();
  }
}

function modelCountMap(rows) {
  const out = new Map();
  for (const row of rows || []) {
    const providerId = String(row.provider_id || '');
    if (!providerId) continue;
    out.set(providerId, (out.get(providerId) || 0) + 1);
  }
  return out;
}

function isPortkeyProviderRow(provider = {}) {
  const baseUrl = String(provider.base_url || provider.baseUrl || '');
  const headers = String(provider.custom_headers || provider.customHeaders || '');
  const type = String(provider.type || '').toLowerCase();
  const id = String(provider.id || '').toLowerCase();
  return type === 'portkey_gateway' ||
    id.includes('portkey') ||
    /(\.|\/\/)portkey\.ai(?:\/|$)/i.test(baseUrl) ||
    /x-portkey-/i.test(headers);
}

function isPortkeyGatewayCredentialRow(provider = {}) {
  const type = String(provider.type || '').toLowerCase();
  const id = String(provider.id || '').toLowerCase();
  return type === 'portkey_gateway' || id.startsWith('portkey-gateway-');
}

function isCatalogProvider(provider = {}, providerModels = []) {
  const id = String(provider.id || '').toLowerCase();
  const name = String(provider.name || '').toLowerCase();
  if (id.includes('-portkey-catalog-') || /\bvia\s+portkey\s+catalog\b/.test(name)) return true;
  return providerModels.length > 0 && providerModels.every((model) => {
    const source = String(model.source || '').toLowerCase();
    const status = String(model.verification_status || '').toLowerCase();
    return source === 'portkey_catalog' || status === 'catalog';
  });
}

function providerRoutePolicy(kv, type) {
  return String(kv.get(`model_provider_route_policy:${type}`) || 'auto');
}

function normalizeProvider(provider, options = {}) {
  const {
    api_key_encrypted: apiKey,
    custom_headers: customHeaders,
    ...safeProvider
  } = provider;
  const isPortkey = isPortkeyProviderRow(provider);
  const isCatalog = !!options.isCatalog;
  const connectionKind = isPortkey ? 'portkey' : (provider.auth_method && provider.auth_method !== 'api_key' ? provider.auth_method : 'direct');
  const hasStoredKey = !!apiKey;
  const hasRuntimeAccess = hasStoredKey || connectionKind === 'portkey' || connectionKind !== 'direct';
  return {
    ...safeProvider,
    has_key: hasRuntimeAccess,
    has_api_key: hasRuntimeAccess,
    has_stored_key: hasStoredKey,
    has_runtime_access: hasRuntimeAccess,
    credential_set: hasRuntimeAccess,
    credential_status: hasRuntimeAccess ? (hasStoredKey ? 'stored' : (connectionKind === 'portkey' ? 'gateway' : 'available')) : 'missing',
    credential_source: hasStoredKey ? 'stored' : (connectionKind === 'portkey' ? 'gateway' : (connectionKind !== 'direct' ? connectionKind : '')),
    is_default_instance: String(provider.id || '').endsWith('-default') || String(provider.id || '').endsWith('-auto'),
    connection_kind: connectionKind,
    connection_source: isCatalog ? 'portkey_catalog' : connectionKind,
    is_catalog: isCatalog,
    catalog_only: isCatalog,
    route_policy: options.routePolicy || 'auto',
    model_catalog_status: 'stale',
    model_count: Number(options.modelCount || 0),
  };
}

function buildGateways(providers, models, kv) {
  const byProvider = new Map();
  for (const model of models) {
    const providerId = String(model.provider_id || '');
    if (!providerId) continue;
    if (!byProvider.has(providerId)) byProvider.set(providerId, []);
    byProvider.get(providerId).push(model);
  }

  const portkeyRows = providers.filter(isPortkeyProviderRow);
  const credentials = portkeyRows.filter(isPortkeyGatewayCredentialRow);
  const routes = portkeyRows
    .filter((provider) => !isPortkeyGatewayCredentialRow(provider))
    .map((provider) => {
      const routeModels = byProvider.get(provider.id) || [];
      const catalogModelCount = routeModels.filter((model) => model.source === 'portkey_catalog').length;
      const modelCount = routeModels.length;
      return {
        id: provider.id,
        name: provider.name,
        type: provider.type,
        enabled: provider.enabled,
        model_count: modelCount,
        catalog_model_count: catalogModelCount,
        gateway_model_count: Math.max(0, modelCount - catalogModelCount),
        supported: modelCount > 0,
        support_status: modelCount > 0 ? (catalogModelCount === modelCount ? 'catalog' : 'supported') : 'unverified',
        sync_error: null,
        route_policy: providerRoutePolicy(kv, provider.type),
      };
    })
    .filter((route) => route.model_count > 0);

  const providerTypes = Array.from(new Set(routes.map((route) => route.type).filter(Boolean))).sort();
  const supportedProviderTypes = providerTypes.filter((type) => routes.some((route) => route.type === type && route.supported));
  const supportedProviders = providerTypes.map((type) => {
    const typeRoutes = routes.filter((route) => route.type === type);
    return {
      type,
      name: displayNameForProviderType(type),
      configured: typeRoutes.length > 0,
      supported: typeRoutes.some((route) => route.supported),
      support_status: typeRoutes.some((route) => route.supported) ? 'supported' : 'unverified',
      route_id: typeRoutes[0]?.id || null,
      model_count: typeRoutes.reduce((sum, route) => sum + route.model_count, 0),
      catalog_model_count: typeRoutes.reduce((sum, route) => sum + route.catalog_model_count, 0),
      gateway_model_count: typeRoutes.reduce((sum, route) => sum + route.gateway_model_count, 0),
      sync_error: null,
      route_policy: providerRoutePolicy(kv, type),
    };
  });
  const policies = {};
  for (const type of providerTypes) policies[type] = providerRoutePolicy(kv, type);

  const totalModelCount = routes.reduce((sum, route) => sum + route.model_count, 0);
  return [{
    type: 'portkey',
    name: 'Portkey Gateway',
    enabled: credentials.some((row) => row.enabled !== 0) || routes.some((route) => route.enabled !== 0),
    configured: credentials.length > 0 || routes.length > 0,
    credential_count: credentials.length || (portkeyRows.length ? 1 : 0),
    route_count: routes.length,
    provider_count: providerTypes.length,
    verified_provider_count: supportedProviderTypes.length,
    model_count: totalModelCount,
    catalog_model_count: routes.reduce((sum, route) => sum + route.catalog_model_count, 0),
    gateway_model_count: routes.reduce((sum, route) => sum + route.gateway_model_count, 0),
    provider_types: providerTypes,
    configured_provider_types: providerTypes,
    supported_provider_types: supportedProviderTypes,
    supported_providers: supportedProviders,
    routes,
    policies,
    last_success_at: kv.get('model_gateway_sync:portkey:last_success_at') || '',
    last_run_at: kv.get('model_gateway_sync:portkey:last_run_at') || '',
    last_error: '',
    last_warning: kv.get('model_gateway_sync:portkey:last_error') || '',
    next_sync_hint: 'hourly',
  }].filter((gateway) => gateway.configured);
}

function readBrainModelOverview(options = {}) {
  const includeModels = options.includeModels !== false;
  const dbPath = options.dbPath || resolveBrainDbPath(options.env || process.env);
  if (!dbPath || !fs.existsSync(dbPath)) return null;
  let Database;
  try {
    Database = require('better-sqlite3');
  } catch {
    return null;
  }
  let db;
  try {
    db = new Database(dbPath, { readonly: true, fileMustExist: true });
    if (!tableExists(db, 'model_providers') || !tableExists(db, 'model_registry')) return null;
    const providersRaw = db.prepare(`
      SELECT id, name, type, base_url, api_key_encrypted, custom_headers, enabled, created_at, updated_at, auth_method
      FROM model_providers
      ORDER BY created_at ASC, id ASC
    `).all();
    const modelRows = includeModels
      ? db.prepare(`
          SELECT id, provider_id, model_id, display_name, capabilities, cost_per_1m_input, cost_per_1m_output,
                 max_context_tokens, max_output_tokens, speed_tier, enabled, is_fine_tuned, source,
                 verification_status, last_seen_at, gateway_type
          FROM model_registry
          ORDER BY provider_id ASC, display_name ASC, model_id ASC
        `).all().map((model) => ({
          ...model,
          capabilities: safeJson(model.capabilities, []),
        }))
      : db.prepare(`
          SELECT id, provider_id, source, verification_status, gateway_type
          FROM model_registry
          ORDER BY provider_id ASC, id ASC
        `).all();
    const counts = modelCountMap(modelRows);
    const byProvider = new Map();
    for (const model of modelRows) {
      if (!byProvider.has(model.provider_id)) byProvider.set(model.provider_id, []);
      byProvider.get(model.provider_id).push(model);
    }
    const kv = kvMap(db);
    const providers = providersRaw
      .filter((provider) => !isPortkeyGatewayCredentialRow(provider))
      .filter((provider) => !(isPortkeyProviderRow(provider) && (counts.get(provider.id) || 0) === 0))
      .map((provider, index) => {
        const providerModels = byProvider.get(provider.id) || [];
        return {
          provider: normalizeProvider(provider, {
            isCatalog: isCatalogProvider(provider, providerModels),
            routePolicy: providerRoutePolicy(kv, provider.type),
            modelCount: counts.get(provider.id) || 0,
          }),
          index,
        };
      });
    providers.sort((a, b) => {
      const rank = Number(!!a.provider.catalog_only) - Number(!!b.provider.catalog_only);
      return rank || (a.index - b.index);
    });
    const normalizedProviders = providers.map((row) => row.provider);
    const typeCounts = {};
    for (const provider of normalizedProviders) typeCounts[provider.type] = (typeCounts[provider.type] || 0) + 1;
    for (const provider of normalizedProviders) provider.instance_count = typeCounts[provider.type];

    const defaults = tableExists(db, 'model_defaults')
      ? { defaults: db.prepare('SELECT task_type, model_registry_id, strategy, quorum_size, updated_at FROM model_defaults ORDER BY task_type').all() }
      : { defaults: [] };
    const shadowEnabled = kv.get('shadow_training_enabled');

    return {
      providers: normalizedProviders,
      models: includeModels ? modelRows : [],
      gateways: buildGateways(providersRaw, modelRows, kv),
      health: {},
      tier_defaults: {},
      defaults,
      shadow_config: { shadow: { enabled: shadowEnabled === undefined ? false : shadowEnabled === '1' || shadowEnabled === 'true' }, counts: {} },
      catalog_status: 'stale',
      catalog_warnings: [{ code: 'wall_e_unavailable', message: 'Loaded the last known AI provider catalog directly from the Wall-E brain database.' }],
      source: 'brain-fallback',
      stale: true,
      generated_at: new Date().toISOString(),
      cache: { fallback: 'brain-db' },
    };
  } catch {
    return null;
  } finally {
    try { if (db) db.close(); } catch {}
  }
}

module.exports = {
  readBrainModelOverview,
  resolveBrainDbPath,
};
