/**
 * ${VOCAB.factory} Hybrid LLM Router
 * Phase 2: Intelligent routing between Local and Cloud LLMs
 * 
 * Strategy:
 * - Local LLMs (Ollama, llama.cpp): Zero cost, privacy, lower capability
 * - Cloud LLMs (Abacus, OpenAI, Grok): High capability, cost-per-token
 * 
 * Route local for:
 * - Simple edits, boilerplate generation
 * - Styling tweaks, code formatting
 * - Quick explanations, documentation
 * 
 * Route cloud for:
 * - Complex architecture decisions
 * - Security-sensitive code
 * - Multi-file refactoring
 * - Novel problem-solving
 */

import { PromptAnalysis } from './prompt-analyzer';
import { MODEL_CAPABILITIES } from './knowledge-base';
import { VOCAB } from '@/lib/white-label/constants';

// ============================================
// ENVIRONMENT CONFIGURATION
// ============================================

/**
 * Read local LLM config from environment variables.
 * Set these in .env to activate the hybrid router:
 *   LOCAL_LLM_ENDPOINT=http://your-ollama-host:11434
 *   LOCAL_LLM_MODEL=qwen3.5:35b          (optional, auto-detected if omitted)
 *   LOCAL_LLM_PROVIDER=ollama             (optional, defaults to ollama)
 *   LOCAL_LLM_ENABLED=true                (master switch, defaults to false)
 *   LOCAL_LLM_PREFER=true                 (route to local when capable, defaults to true)
 *   LOCAL_LLM_QUALITY_THRESHOLD=0.7       (0-1, below this always use cloud)
 *   LOCAL_LLM_COST_THRESHOLD=100000       (tokens/day before forcing local)
 */
function getEnvConfig(): {
  enabled: boolean;
  endpoint: string;
  model: string | undefined;
  provider: 'ollama' | 'llamacpp' | 'lmstudio';
  preferLocal: boolean;
  qualityThreshold: number;
  costThreshold: number;
} {
  const enabled = process.env.LOCAL_LLM_ENABLED === 'true';
  const endpoint = process.env.LOCAL_LLM_ENDPOINT || 'http://localhost:11434';
  const model = process.env.LOCAL_LLM_MODEL || undefined;
  const provider = (process.env.LOCAL_LLM_PROVIDER || 'ollama') as 'ollama' | 'llamacpp' | 'lmstudio';
  const preferLocal = process.env.LOCAL_LLM_PREFER !== 'false'; // default true
  const qualityThreshold = parseFloat(process.env.LOCAL_LLM_QUALITY_THRESHOLD || '0.7');
  const costThreshold = parseInt(process.env.LOCAL_LLM_COST_THRESHOLD || '100000', 10);

  return { enabled, endpoint, model, provider, preferLocal, qualityThreshold, costThreshold };
}

// ============================================
// TYPES
// ============================================

export type LLMEnvironment = 'local' | 'cloud' | 'hybrid';

export interface LocalLLMConfig {
  provider: 'ollama' | 'llamacpp' | 'lmstudio';
  endpoint: string;
  model: string;
  available: boolean;
  capabilities: {
    maxTokens: number;
    codeGeneration: 'basic' | 'good' | 'excellent';
    reasoning: 'basic' | 'good' | 'excellent';
  };
}

export interface CloudLLMConfig {
  provider: 'abacus' | 'openai' | 'grok';
  model: string;
  costPerMToken: number; // Cost per million tokens
  capabilities: {
    maxTokens: number;
    codeGeneration: 'basic' | 'good' | 'excellent';
    reasoning: 'basic' | 'good' | 'excellent';
  };
}

export interface HybridRoutingDecision {
  environment: LLMEnvironment;
  provider: string;
  model: string;
  reasoning: string;
  estimatedCost: number;
  qualityScore: number;
  fallbackChain: Array<{ environment: LLMEnvironment; model: string }>;
}

export interface HybridConfig {
  preferLocal: boolean;          // Default to local when possible
  localEndpoint?: string;        // Ollama/LM Studio endpoint
  localModel?: string;           // Default local model
  qualityThreshold: number;      // 0-1, below this use cloud
  costThreshold: number;         // Max tokens/day before forcing local
  alwaysCloudFor: string[];      // Task types that always use cloud
  alwaysLocalFor: string[];      // Task types that always use local
}

// ============================================
// DEFAULT CONFIGURATIONS
// ============================================

function getDefaultLocalConfig(): LocalLLMConfig {
  const env = getEnvConfig();
  return {
    provider: env.provider,
    endpoint: env.endpoint,
    model: env.model || 'codellama:13b',
    available: false, // Set dynamically
    capabilities: {
      maxTokens: 4096,
      codeGeneration: 'good',
      reasoning: 'basic'
    }
  };
}

// Keep a static reference for backward compat
const DEFAULT_LOCAL_CONFIG: LocalLLMConfig = {
  provider: 'ollama',
  endpoint: 'http://localhost:11434',
  model: 'codellama:13b',
  available: false,
  capabilities: {
    maxTokens: 4096,
    codeGeneration: 'good',
    reasoning: 'basic'
  }
};

const LOCAL_MODELS: Record<string, LocalLLMConfig> = {
  'codellama:13b': {
    provider: 'ollama',
    endpoint: 'http://localhost:11434',
    model: 'codellama:13b',
    available: false,
    capabilities: {
      maxTokens: 4096,
      codeGeneration: 'good',
      reasoning: 'basic'
    }
  },
  'deepseek-coder:33b': {
    provider: 'ollama',
    endpoint: 'http://localhost:11434',
    model: 'deepseek-coder:33b',
    available: false,
    capabilities: {
      maxTokens: 8192,
      codeGeneration: 'excellent',
      reasoning: 'good'
    }
  },
  'qwen2.5-coder:14b': {
    provider: 'ollama',
    endpoint: 'http://localhost:11434',
    model: 'qwen2.5-coder:14b',
    available: false,
    capabilities: {
      maxTokens: 8192,
      codeGeneration: 'good',
      reasoning: 'good'
    }
  },
  'llama3.3:70b': {
    provider: 'ollama',
    endpoint: 'http://localhost:11434',
    model: 'llama3.3:70b',
    available: false,
    capabilities: {
      maxTokens: 8192,
      codeGeneration: 'good',
      reasoning: 'excellent'
    }
  }
};

const CLOUD_MODELS: Record<string, CloudLLMConfig> = {
  'claude-haiku-4.5': {
    provider: 'abacus',
    model: 'claude-haiku-4.5',
    costPerMToken: 0.25, // Cheap
    capabilities: {
      maxTokens: 200000,
      codeGeneration: 'good',
      reasoning: 'good'
    }
  },
  'claude-sonnet-4.6': {
    provider: 'abacus',
    model: 'claude-sonnet-4.6',
    costPerMToken: 3.0, // Medium
    capabilities: {
      maxTokens: 200000,
      codeGeneration: 'excellent',
      reasoning: 'excellent'
    }
  },
  'claude-opus-4.6': {
    provider: 'abacus',
    model: 'claude-opus-4.6',
    costPerMToken: 15.0, // Expensive
    capabilities: {
      maxTokens: 200000,
      codeGeneration: 'excellent',
      reasoning: 'excellent'
    }
  },
  'deepseek-v3.1': {
    provider: 'abacus',
    model: 'deepseek-v3.1',
    costPerMToken: 0.14, // Very cheap
    capabilities: {
      maxTokens: 64000,
      codeGeneration: 'excellent',
      reasoning: 'good'
    }
  },
  'gpt-4.1': {
    provider: 'abacus',
    model: 'gpt-4.1',
    costPerMToken: 2.5, // Medium
    capabilities: {
      maxTokens: 128000,
      codeGeneration: 'excellent',
      reasoning: 'excellent'
    }
  }
};

const DEFAULT_HYBRID_CONFIG: HybridConfig = {
  preferLocal: true,
  localEndpoint: 'http://localhost:11434',
  localModel: 'codellama:13b',
  qualityThreshold: 0.7,
  costThreshold: 100000, // 100K tokens/day
  alwaysCloudFor: ['security', 'architecture', 'complex-refactor'],
  alwaysLocalFor: ['styling', 'formatting', 'simple-edit', 'documentation']
};

// ============================================
// LOCAL LLM DETECTION
// ============================================

let localAvailabilityCache: { available: boolean; models: string[]; checkedAt: Date } | null = null;
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

/**
 * Check if local LLM is available (Ollama, LM Studio, etc.)
 * Respects LOCAL_LLM_ENABLED env var — if false, skips detection entirely.
 */
export async function checkLocalAvailability(
  endpoint?: string
): Promise<{ available: boolean; models: string[] }> {
  const env = getEnvConfig();
  
  // Master switch — skip detection if disabled
  if (!env.enabled) {
    return { available: false, models: [] };
  }
  
  const resolvedEndpoint = endpoint || env.endpoint;
  // Check cache
  if (localAvailabilityCache && 
      Date.now() - localAvailabilityCache.checkedAt.getTime() < CACHE_TTL) {
    return {
      available: localAvailabilityCache.available,
      models: localAvailabilityCache.models
    };
  }
  
  try {
    // Try Ollama API
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 2000); // 2s timeout
    
    const response = await fetch(`${resolvedEndpoint}/api/tags`, {
      signal: controller.signal
    });
    
    clearTimeout(timeoutId);
    
    if (response.ok) {
      const data = await response.json();
      const models = data.models?.map((m: any) => m.name) || [];
      
      localAvailabilityCache = {
        available: true,
        models,
        checkedAt: new Date()
      };
      
      return { available: true, models };
    }
  } catch {
    // Local not available
  }
  
  localAvailabilityCache = {
    available: false,
    models: [],
    checkedAt: new Date()
  };
  
  return { available: false, models: [] };
}

/**
 * Get the best available local model.
 * If LOCAL_LLM_MODEL is set, uses that directly (skips auto-detection of best model).
 */
export async function getBestLocalModel(
  endpoint?: string
): Promise<LocalLLMConfig | null> {
  const env = getEnvConfig();
  const resolvedEndpoint = endpoint || env.endpoint;
  const { available, models } = await checkLocalAvailability(resolvedEndpoint);
  
  if (!available || models.length === 0) {
    return null;
  }
  
  // If user explicitly set LOCAL_LLM_MODEL, honor it
  if (env.model && models.some(m => m.includes(env.model!.split(':')[0]))) {
    const matched = models.find(m => m.includes(env.model!.split(':')[0]))!;
    return {
      provider: env.provider,
      endpoint: resolvedEndpoint,
      model: matched,
      available: true,
      capabilities: LOCAL_MODELS[env.model]?.capabilities || {
        maxTokens: 8192,
        codeGeneration: 'good',
        reasoning: 'good'
      }
    };
  }
  
  // Priority order for code generation (2026 models)
  const preferredModels = [
    'qwen3.5:35b',
    'deepseek-v3.2:35b',
    'glm-5:35b',
    'deepseek-coder:33b',
    'qwen2.5-coder:14b',
    'codellama:13b',
    'llama3.3:70b',
    'llama3.2:3b' // Fallback to smaller model
  ];
  
  for (const preferred of preferredModels) {
    if (models.some(m => m.includes(preferred.split(':')[0]))) {
      const fullModel = models.find(m => m.includes(preferred.split(':')[0]))!;
      return {
        ...LOCAL_MODELS[preferred] || DEFAULT_LOCAL_CONFIG,
        model: fullModel,
        available: true
      };
    }
  }
  
  // Return first available model
  return {
    ...DEFAULT_LOCAL_CONFIG,
    model: models[0],
    available: true
  };
}

// ============================================
// HYBRID ROUTING LOGIC
// ============================================

/**
 * Determine whether to use local or cloud LLM
 */
export async function routeToEnvironment(
  analysis: PromptAnalysis,
  config: Partial<HybridConfig> = {}
): Promise<HybridRoutingDecision> {
  const env = getEnvConfig();
  const hybridConfig = {
    ...DEFAULT_HYBRID_CONFIG,
    preferLocal: env.preferLocal,
    localEndpoint: env.endpoint,
    localModel: env.model,
    qualityThreshold: env.qualityThreshold,
    costThreshold: env.costThreshold,
    ...config  // Caller overrides still win
  };
  const reasoning: string[] = [];
  
  // Check local availability (respects LOCAL_LLM_ENABLED)
  const localModel = await getBestLocalModel(hybridConfig.localEndpoint);
  const localAvailable = localModel !== null;
  
  // ===============================
  // RULE 1: Always-cloud tasks
  // ===============================
  const isSecurityTask = analysis.domain.includes('security');
  const isHighComplexity = analysis.complexity === 'high';
  const isMultiDomain = analysis.domain.length >= 3;
  
  if (isSecurityTask && (analysis.taskType === 'create' || analysis.taskType === 'refactor')) {
    reasoning.push('Security-critical task requires cloud LLM for accuracy');
    return createCloudDecision('claude-opus-4.6', reasoning, analysis);
  }
  
  if (isHighComplexity && isMultiDomain) {
    reasoning.push('High complexity multi-domain task requires advanced reasoning');
    return createCloudDecision('claude-opus-4.6', reasoning, analysis);
  }
  
  // ===============================
  // RULE 2: Always-local tasks (if available)
  // ===============================
  if (localAvailable) {
    const isSimpleStyling = analysis.complexity === 'low' && 
                            analysis.domain.length === 1 && 
                            analysis.domain[0] === 'styling';
    const isSimpleEdit = analysis.complexity === 'low' && analysis.taskType === 'edit';
    const isExplanation = analysis.taskType === 'explain' && analysis.complexity !== 'high';
    
    if (isSimpleStyling || isSimpleEdit || isExplanation) {
      reasoning.push('Simple task suitable for local LLM (zero cost)');
      return createLocalDecision(localModel!, reasoning, analysis);
    }
  }
  
  // ===============================
  // RULE 3: Prefer local if configured and capable
  // ===============================
  if (hybridConfig.preferLocal && localAvailable) {
    const localCapable = canLocalHandle(analysis, localModel!);
    
    if (localCapable) {
      reasoning.push('Task within local LLM capabilities (preferLocal=true)');
      return createLocalDecision(localModel!, reasoning, analysis);
    } else {
      reasoning.push('Task exceeds local LLM capabilities, routing to cloud');
    }
  }
  
  // ===============================
  // RULE 4: Default cloud routing
  // ===============================
  if (!localAvailable) {
    reasoning.push('Local LLM not available, using cloud');
  }
  
  // Select appropriate cloud model based on task
  const cloudModel = selectCloudModel(analysis);
  return createCloudDecision(cloudModel, reasoning, analysis);
}

/**
 * Check if local LLM can handle the task
 */
function canLocalHandle(analysis: PromptAnalysis, localModel: LocalLLMConfig): boolean {
  // Token check
  if (analysis.estimatedTokens > localModel.capabilities.maxTokens) {
    return false;
  }
  
  // Complexity check
  if (analysis.complexity === 'high') {
    return false;
  }
  
  // Task type check
  const localSuitableTasks = ['edit', 'explain', 'test'];
  if (!localSuitableTasks.includes(analysis.taskType) && analysis.complexity !== 'low') {
    return false;
  }
  
  // Domain check - security always goes cloud
  if (analysis.domain.includes('security')) {
    return false;
  }
  
  return true;
}

/**
 * Select the best cloud model for the task
 */
function selectCloudModel(analysis: PromptAnalysis): string {
  // Cost-effective options for simple tasks
  if (analysis.complexity === 'low') {
    return 'deepseek-v3.1'; // Cheapest
  }
  
  // Medium complexity
  if (analysis.complexity === 'medium') {
    if (analysis.taskType === 'debug') {
      return 'claude-sonnet-4.6'; // Good at debugging
    }
    return 'claude-haiku-4.5'; // Good balance
  }
  
  // High complexity
  if (analysis.domain.includes('security') || analysis.domain.length >= 3) {
    return 'claude-opus-4.6'; // Best for complex tasks
  }
  
  return 'claude-sonnet-4.6'; // Default
}

// ============================================
// DECISION BUILDERS
// ============================================

function createLocalDecision(
  localModel: LocalLLMConfig,
  reasoning: string[],
  analysis: PromptAnalysis
): HybridRoutingDecision {
  return {
    environment: 'local',
    provider: localModel.provider,
    model: localModel.model,
    reasoning: reasoning.join(' → '),
    estimatedCost: 0, // Free!
    qualityScore: getQualityScore('local', analysis),
    fallbackChain: [
      { environment: 'cloud', model: 'deepseek-v3.1' },
      { environment: 'cloud', model: 'claude-haiku-4.5' }
    ]
  };
}

function createCloudDecision(
  model: string,
  reasoning: string[],
  analysis: PromptAnalysis
): HybridRoutingDecision {
  const cloudConfig = CLOUD_MODELS[model] || CLOUD_MODELS['claude-sonnet-4.6'];
  const estimatedTokens = analysis.estimatedTokens * 3; // Include response
  const estimatedCost = (estimatedTokens / 1_000_000) * cloudConfig.costPerMToken;
  
  return {
    environment: 'cloud',
    provider: cloudConfig.provider,
    model: cloudConfig.model,
    reasoning: reasoning.join(' → '),
    estimatedCost,
    qualityScore: getQualityScore('cloud', analysis),
    fallbackChain: buildCloudFallbackChain(model)
  };
}

function getQualityScore(env: LLMEnvironment, analysis: PromptAnalysis): number {
  const baseScore = env === 'cloud' ? 0.9 : 0.7;
  
  // Adjust for task complexity
  if (analysis.complexity === 'high') {
    return env === 'cloud' ? 0.95 : 0.5;
  }
  
  if (analysis.complexity === 'low') {
    return env === 'cloud' ? 0.95 : 0.85;
  }
  
  return baseScore;
}

function buildCloudFallbackChain(primaryModel: string): Array<{ environment: LLMEnvironment; model: string }> {
  const fallbacks: Record<string, string[]> = {
    'claude-opus-4.6': ['claude-sonnet-4.6', 'gpt-4.1'],
    'claude-sonnet-4.6': ['claude-haiku-4.5', 'deepseek-v3.1'],
    'claude-haiku-4.5': ['deepseek-v3.1'],
    'deepseek-v3.1': ['claude-haiku-4.5'],
    'gpt-4.1': ['claude-sonnet-4.6']
  };
  
  const chain = fallbacks[primaryModel] || ['claude-sonnet-4.6'];
  return chain.map(model => ({ environment: 'cloud' as LLMEnvironment, model }));
}

// ============================================
// UNIFIED HYBRID API
// ============================================

/**
 * Call the appropriate LLM based on hybrid routing
 */
export async function callHybridLLM(
  messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>,
  decision: HybridRoutingDecision,
  options: { temperature?: number; maxTokens?: number } = {}
): Promise<{ content: string; tokensUsed: number; cost: number; environment: LLMEnvironment }> {
  const { temperature = 0.3, maxTokens = 4000 } = options;
  
  if (decision.environment === 'local') {
    return callLocalLLM(messages, decision.model, { temperature, maxTokens });
  } else {
    return callCloudLLM(messages, decision.model, { temperature, maxTokens });
  }
}

async function callLocalLLM(
  messages: Array<{ role: string; content: string }>,
  model: string,
  options: { temperature: number; maxTokens: number }
): Promise<{ content: string; tokensUsed: number; cost: number; environment: LLMEnvironment }> {
  const env = getEnvConfig();
  const endpoint = env.endpoint;
  
  try {
    const response = await fetch(`${endpoint}/api/chat`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        model,
        messages,
        stream: false,
        options: {
          temperature: options.temperature,
          num_predict: options.maxTokens
        }
      })
    });
    
    if (!response.ok) {
      throw new Error(`Local LLM error: ${response.status}`);
    }
    
    const data = await response.json();
    const content = data.message?.content || '';
    const tokensUsed = data.eval_count || Math.ceil(content.length / 4);
    
    return {
      content,
      tokensUsed,
      cost: 0, // Free!
      environment: 'local'
    };
  } catch (error) {
    throw new Error(`Local LLM unavailable: ${error}`);
  }
}

async function callCloudLLM(
  messages: Array<{ role: string; content: string }>,
  model: string,
  options: { temperature: number; maxTokens: number }
): Promise<{ content: string; tokensUsed: number; cost: number; environment: LLMEnvironment }> {
  const apiKey = process.env.ABACUSAI_API_KEY;
  
  if (!apiKey) {
    throw new Error('ABACUSAI_API_KEY not configured');
  }
  
  const response = await fetch('https://routellm.abacus.ai/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`
    },
    body: JSON.stringify({
      model,
      messages,
      max_tokens: options.maxTokens,
      temperature: options.temperature
    })
  });
  
  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Cloud LLM error: ${error}`);
  }
  
  const data = await response.json();
  const content = data.choices?.[0]?.message?.content || '';
  const tokensUsed = data.usage?.total_tokens || Math.ceil(content.length / 4);
  const cloudConfig = CLOUD_MODELS[model] || CLOUD_MODELS['claude-sonnet-4.6'];
  const cost = (tokensUsed / 1_000_000) * cloudConfig.costPerMToken;
  
  return {
    content,
    tokensUsed,
    cost,
    environment: 'cloud'
  };
}

// ============================================
// STATISTICS & REPORTING
// ============================================

interface HybridStats {
  totalRequests: number;
  localRequests: number;
  cloudRequests: number;
  totalTokens: number;
  totalCost: number;
  costSaved: number;
}

const stats: HybridStats = {
  totalRequests: 0,
  localRequests: 0,
  cloudRequests: 0,
  totalTokens: 0,
  totalCost: 0,
  costSaved: 0
};

export function recordHybridUsage(
  environment: LLMEnvironment,
  tokens: number,
  cost: number
): void {
  stats.totalRequests++;
  stats.totalTokens += tokens;
  
  if (environment === 'local') {
    stats.localRequests++;
    // Estimate savings vs cloud
    const cloudCost = (tokens / 1_000_000) * 3; // Assume ~$3/M avg
    stats.costSaved += cloudCost;
  } else {
    stats.cloudRequests++;
    stats.totalCost += cost;
  }
}

export function getHybridStats(): HybridStats & { localPercentage: number } {
  return {
    ...stats,
    localPercentage: stats.totalRequests > 0 
      ? (stats.localRequests / stats.totalRequests) * 100 
      : 0
  };
}

export function formatHybridStats(): string {
  const s = getHybridStats();
  return `
🔀 Hybrid LLM Stats:
  Total Requests: ${s.totalRequests}
  Local: ${s.localRequests} (${s.localPercentage.toFixed(1)}%)
  Cloud: ${s.cloudRequests}
  Total Tokens: ${s.totalTokens.toLocaleString()}
  Cloud Cost: $${s.totalCost.toFixed(4)}
  Estimated Savings: $${s.costSaved.toFixed(4)}
  `.trim();
}
