/**
 * ${VOCAB.factory} Token Conservation Engine
 * Phase 2: Intelligent Token Management
 * 
 * Strategies:
 * 1. Prompt Compression - Remove redundancy, normalize whitespace
 * 2. Response Caching - Store common pattern responses
 * 3. Context Summarization - Condense large knowledge entries
 * 4. Token Budget Tracking - Per-user/project limits
 */

import { KnowledgeEntry } from './knowledge-base';
import { VOCAB } from '@/lib/white-label/constants';

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

export interface TokenBudget {
  userId: string;
  projectId?: string;
  dailyLimit: number;
  dailyUsed: number;
  monthlyLimit: number;
  monthlyUsed: number;
  lastReset: Date;
}

export interface CompressionResult {
  originalTokens: number;
  compressedTokens: number;
  compressionRatio: number;
  compressedPrompt: string;
}

export interface CacheEntry {
  key: string;
  promptHash: string;
  response: string;
  model: string;
  tokensSaved: number;
  createdAt: Date;
  hitCount: number;
  lastHit: Date;
}

export interface ConservationStats {
  promptCompressionSavings: number;
  cacheSavings: number;
  contextSummarizationSavings: number;
  totalSavings: number;
  percentageSaved: number;
}

// ============================================
// PROMPT COMPRESSION
// ============================================

/**
 * Compress a prompt while preserving meaning
 */
export function compressPrompt(prompt: string): CompressionResult {
  const originalTokens = estimateTokenCount(prompt);
  let compressed = prompt;
  
  // 1. Normalize whitespace
  compressed = compressed.replace(/\s+/g, ' ').trim();
  
  // 2. Remove filler phrases
  const fillerPhrases = [
    'i want you to',
    'can you please',
    'i would like',
    'i need you to',
    'please help me',
    'could you',
    'would you mind',
    'i was wondering if',
    'it would be great if',
    'basically what i need is'
  ];
  
  const promptLower = compressed.toLowerCase();
  for (const filler of fillerPhrases) {
    if (promptLower.startsWith(filler)) {
      compressed = compressed.substring(filler.length).trim();
      break;
    }
  }
  
  // 3. Normalize common patterns
  compressed = compressed
    .replace(/\bplease\b/gi, '')
    .replace(/\bthanks\b/gi, '')
    .replace(/\bthank you\b/gi, '')
    .replace(/\s+/g, ' ')
    .trim();
  
  // 4. Contract code references (preserve actual code blocks)
  compressed = compressed.replace(/```([\s\S]*?)```/g, (match, code) => {
    // Keep code blocks but compress internal whitespace
    const compressedCode = code.replace(/\n\s*\n/g, '\n').trim();
    return '```' + compressedCode + '```';
  });
  
  const compressedTokens = estimateTokenCount(compressed);
  
  return {
    originalTokens,
    compressedTokens,
    compressionRatio: originalTokens > 0 ? compressedTokens / originalTokens : 1,
    compressedPrompt: compressed
  };
}

// ============================================
// RESPONSE CACHING
// ============================================

// In-memory cache (would be Redis/DB in production)
const responseCache = new Map<string, CacheEntry>();
const CACHE_MAX_SIZE = 500;
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours

/**
 * Generate a cache key from prompt and context
 */
export function generateCacheKey(prompt: string, patterns: string[], model: string): string {
  const normalized = prompt.toLowerCase().replace(/\s+/g, ' ').trim();
  const patternsKey = patterns.sort().join(',');
  return `${hashString(normalized)}:${patternsKey}:${model}`;
}

/**
 * Check if a cached response exists
 */
export function getCachedResponse(cacheKey: string): CacheEntry | null {
  const entry = responseCache.get(cacheKey);
  
  if (!entry) return null;
  
  // Check TTL
  if (Date.now() - entry.createdAt.getTime() > CACHE_TTL_MS) {
    responseCache.delete(cacheKey);
    return null;
  }
  
  // Update hit stats
  entry.hitCount++;
  entry.lastHit = new Date();
  
  return entry;
}

/**
 * Store a response in cache
 */
export function cacheResponse(
  cacheKey: string,
  prompt: string,
  response: string,
  model: string
): void {
  // Evict oldest entries if at capacity
  if (responseCache.size >= CACHE_MAX_SIZE) {
    evictOldestEntries(Math.floor(CACHE_MAX_SIZE * 0.1)); // Remove 10%
  }
  
  const entry: CacheEntry = {
    key: cacheKey,
    promptHash: hashString(prompt),
    response,
    model,
    tokensSaved: estimateTokenCount(response),
    createdAt: new Date(),
    hitCount: 0,
    lastHit: new Date()
  };
  
  responseCache.set(cacheKey, entry);
}

function evictOldestEntries(count: number): void {
  const entries = Array.from(responseCache.entries())
    .sort((a, b) => a[1].lastHit.getTime() - b[1].lastHit.getTime());
  
  for (let i = 0; i < Math.min(count, entries.length); i++) {
    responseCache.delete(entries[i][0]);
  }
}

/**
 * Get cache statistics
 */
export function getCacheStats(): { size: number; totalHits: number; totalSaved: number } {
  let totalHits = 0;
  let totalSaved = 0;
  
  for (const entry of responseCache.values()) {
    totalHits += entry.hitCount;
    totalSaved += entry.tokensSaved * entry.hitCount;
  }
  
  return {
    size: responseCache.size,
    totalHits,
    totalSaved
  };
}

// ============================================
// CONTEXT SUMMARIZATION
// ============================================

/**
 * Summarize knowledge entries to reduce context size
 * Uses extractive summarization (picks key lines)
 */
export function summarizeKnowledge(
  entries: KnowledgeEntry[],
  maxTokensPerEntry: number = 200
): KnowledgeEntry[] {
  return entries.map(entry => {
    const contentTokens = estimateTokenCount(entry.content);
    
    if (contentTokens <= maxTokensPerEntry) {
      return entry; // Already within budget
    }
    
    // Extract key parts
    const summarized = extractKeyContent(entry.content, maxTokensPerEntry);
    
    return {
      ...entry,
      content: summarized,
      description: entry.description + ' (summarized)'
    };
  });
}

function extractKeyContent(content: string, maxTokens: number): string {
  const lines = content.split('\n').filter(l => l.trim());
  const result: string[] = [];
  let currentTokens = 0;
  
  // Priority: code examples, type definitions, key patterns
  const priorityPatterns = [
    /^(export|import|const|function|interface|type|class)/,
    /^\s*(return|await|async)/,
    /^\s*\/\//  // Comments often explain intent
  ];
  
  // First pass: high-priority lines
  for (const line of lines) {
    if (priorityPatterns.some(p => p.test(line.trim()))) {
      const lineTokens = estimateTokenCount(line);
      if (currentTokens + lineTokens <= maxTokens * 0.7) {
        result.push(line);
        currentTokens += lineTokens;
      }
    }
  }
  
  // Second pass: fill remaining with other lines
  for (const line of lines) {
    if (!result.includes(line)) {
      const lineTokens = estimateTokenCount(line);
      if (currentTokens + lineTokens <= maxTokens) {
        result.push(line);
        currentTokens += lineTokens;
      }
    }
  }
  
  return result.join('\n');
}

// ============================================
// TOKEN BUDGET TRACKING
// ============================================

// In-memory budget store (would be DB in production)
const budgetStore = new Map<string, TokenBudget>();

const DEFAULT_DAILY_LIMIT = 50000;   // 50K tokens/day free tier
const DEFAULT_MONTHLY_LIMIT = 500000; // 500K tokens/month free tier

/**
 * Get or create a token budget for a user
 */
export function getTokenBudget(userId: string, projectId?: string): TokenBudget {
  const key = projectId ? `${userId}:${projectId}` : userId;
  
  let budget = budgetStore.get(key);
  
  if (!budget) {
    budget = {
      userId,
      projectId,
      dailyLimit: DEFAULT_DAILY_LIMIT,
      dailyUsed: 0,
      monthlyLimit: DEFAULT_MONTHLY_LIMIT,
      monthlyUsed: 0,
      lastReset: new Date()
    };
    budgetStore.set(key, budget);
  }
  
  // Check for daily reset
  const now = new Date();
  const lastReset = new Date(budget.lastReset);
  
  if (now.toDateString() !== lastReset.toDateString()) {
    budget.dailyUsed = 0;
    budget.lastReset = now;
  }
  
  // Check for monthly reset
  if (now.getMonth() !== lastReset.getMonth() || now.getFullYear() !== lastReset.getFullYear()) {
    budget.monthlyUsed = 0;
  }
  
  return budget;
}

/**
 * Record token usage
 */
export function recordTokenUsage(userId: string, tokens: number, projectId?: string): boolean {
  const budget = getTokenBudget(userId, projectId);
  
  // Check limits
  if (budget.dailyUsed + tokens > budget.dailyLimit) {
    return false; // Daily limit exceeded
  }
  
  if (budget.monthlyUsed + tokens > budget.monthlyLimit) {
    return false; // Monthly limit exceeded
  }
  
  // Record usage
  budget.dailyUsed += tokens;
  budget.monthlyUsed += tokens;
  
  return true;
}

/**
 * Check if user has budget remaining
 */
export function hasBudgetRemaining(userId: string, requiredTokens: number, projectId?: string): boolean {
  const budget = getTokenBudget(userId, projectId);
  return (
    budget.dailyUsed + requiredTokens <= budget.dailyLimit &&
    budget.monthlyUsed + requiredTokens <= budget.monthlyLimit
  );
}

/**
 * Upgrade user budget (e.g., for pro tier)
 */
export function upgradeBudget(userId: string, tier: 'pro' | 'team', projectId?: string): void {
  const budget = getTokenBudget(userId, projectId);
  
  if (tier === 'pro') {
    budget.dailyLimit = 200000;    // 200K/day
    budget.monthlyLimit = 2000000; // 2M/month
  } else if (tier === 'team') {
    budget.dailyLimit = 500000;    // 500K/day
    budget.monthlyLimit = 5000000; // 5M/month
  }
}

// ============================================
// UNIFIED CONSERVATION API
// ============================================

export interface ConservationOptions {
  enableCompression?: boolean;
  enableCaching?: boolean;
  enableSummarization?: boolean;
  maxContextTokens?: number;
  userId?: string;
  projectId?: string;
}

/**
 * Apply all conservation strategies to a request
 */
export function applyConservation(
  prompt: string,
  knowledge: KnowledgeEntry[],
  model: string,
  patterns: string[],
  options: ConservationOptions = {}
): {
  optimizedPrompt: string;
  optimizedKnowledge: KnowledgeEntry[];
  cachedResponse: CacheEntry | null;
  stats: ConservationStats;
  budgetOk: boolean;
} {
  const {
    enableCompression = true,
    enableCaching = true,
    enableSummarization = true,
    maxContextTokens = 200,
    userId,
    projectId
  } = options;
  
  let optimizedPrompt = prompt;
  let optimizedKnowledge = knowledge;
  let cachedResponse: CacheEntry | null = null;
  let promptCompressionSavings = 0;
  let cacheSavings = 0;
  let contextSummarizationSavings = 0;
  
  // 1. Check cache first
  if (enableCaching) {
    const cacheKey = generateCacheKey(prompt, patterns, model);
    cachedResponse = getCachedResponse(cacheKey);
    
    if (cachedResponse) {
      cacheSavings = cachedResponse.tokensSaved;
    }
  }
  
  // 2. Compress prompt
  if (enableCompression && !cachedResponse) {
    const compression = compressPrompt(prompt);
    optimizedPrompt = compression.compressedPrompt;
    promptCompressionSavings = compression.originalTokens - compression.compressedTokens;
  }
  
  // 3. Summarize knowledge context
  if (enableSummarization && !cachedResponse) {
    const originalTokens = knowledge.reduce(
      (sum, k) => sum + estimateTokenCount(k.content), 0
    );
    
    optimizedKnowledge = summarizeKnowledge(knowledge, maxContextTokens);
    
    const newTokens = optimizedKnowledge.reduce(
      (sum, k) => sum + estimateTokenCount(k.content), 0
    );
    
    contextSummarizationSavings = originalTokens - newTokens;
  }
  
  // 4. Check budget
  const estimatedTokens = estimateTokenCount(optimizedPrompt) +
    optimizedKnowledge.reduce((sum, k) => sum + estimateTokenCount(k.content), 0);
  
  const budgetOk = userId 
    ? hasBudgetRemaining(userId, estimatedTokens * 3, projectId) // 3x for response
    : true;
  
  const totalSavings = promptCompressionSavings + cacheSavings + contextSummarizationSavings;
  const originalTokens = estimateTokenCount(prompt) +
    knowledge.reduce((sum, k) => sum + estimateTokenCount(k.content), 0);
  
  return {
    optimizedPrompt,
    optimizedKnowledge,
    cachedResponse,
    stats: {
      promptCompressionSavings,
      cacheSavings,
      contextSummarizationSavings,
      totalSavings,
      percentageSaved: originalTokens > 0 ? (totalSavings / originalTokens) * 100 : 0
    },
    budgetOk
  };
}

// ============================================
// UTILITIES
// ============================================

/**
 * Estimate token count (roughly 4 chars per token)
 */
export function estimateTokenCount(text: string): number {
  return Math.ceil(text.length / 4);
}

/**
 * Simple hash function for cache keys
 */
function hashString(str: string): string {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = ((hash << 5) - hash) + char;
    hash = hash & hash; // Convert to 32bit integer
  }
  return Math.abs(hash).toString(36);
}

/**
 * Format token savings for display
 */
export function formatTokenSavings(stats: ConservationStats): string {
  const parts: string[] = [];
  
  if (stats.promptCompressionSavings > 0) {
    parts.push(`Compression: ${stats.promptCompressionSavings} tokens`);
  }
  if (stats.cacheSavings > 0) {
    parts.push(`Cache hit: ${stats.cacheSavings} tokens`);
  }
  if (stats.contextSummarizationSavings > 0) {
    parts.push(`Context: ${stats.contextSummarizationSavings} tokens`);
  }
  
  return `💰 Saved ${stats.totalSavings} tokens (${stats.percentageSaved.toFixed(1)}%) | ` +
         parts.join(' | ');
}
