/**
 * RIE — RAG Knowledge Base Client
 *
 * Queries the Advanced Sports RAG service (24K+ chunks of sports analytics books)
 * for relevant statistical theory and domain knowledge per matchup.
 *
 * Endpoint: POST http://127.0.0.1:5001/search
 * Auth: X-API-Key: eventheodds-flask-api-key-2025
 */

import { RagInsight } from './types';

const RAG_URL = 'http://127.0.0.1:5001/search';
const RAG_API_KEY = 'eventheodds-flask-api-key-2025';
const RAG_TIMEOUT_MS = 2500;
const CACHE_TTL_MS = 6 * 60 * 60 * 1000; // 6 hours

// In-memory cache keyed by query hash
const cache = new Map<string, { result: RagInsight; expiresAt: number }>();

function cacheKey(league: string, homeShort: string, awayShort: string, query: string): string {
  return `${league}:${homeShort}:${awayShort}:${query.slice(0, 60)}`;
}

async function fetchRag(query: string, k: number = 3): Promise<Array<{ content: string; source: string; score: number }>> {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), RAG_TIMEOUT_MS);

  try {
    const res = await fetch(RAG_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': RAG_API_KEY,
      },
      body: JSON.stringify({ query, top_k: k }),
      signal: controller.signal,
    });

    if (!res.ok) return [];

    const data: any = await res.json();
    const results = data.results || data.documents || [];

    return results.slice(0, k).map((r: any) => ({
      content: (r.content || r.text || '').slice(0, 500),
      source: r.source || r.metadata?.source || 'unknown',
      score: r.score || r.similarity || 0,
    }));
  } catch {
    return [];
  } finally {
    clearTimeout(timeout);
  }
}

function summarizeChunks(chunks: Array<{ content: string; source: string; score: number }>): string {
  if (chunks.length === 0) return '';
  // Extract the key concept from the top chunk
  const top = chunks[0].content;
  // Take first sentence or first 150 chars
  const firstSentence = top.split(/[.!?]\s/)[0];
  return firstSentence ? firstSentence.slice(0, 150) : top.slice(0, 150);
}

/**
 * Query the RAG knowledge base with multiple queries in parallel.
 * Results are cached per matchup + query for 6 hours.
 */
export async function queryRag(params: {
  queries: string[];
  league: string;
  homeShort: string;
  awayShort: string;
  k?: number;
}): Promise<RagInsight[]> {
  const { queries, league, homeShort, awayShort, k = 3 } = params;
  const now = Date.now();

  // Check cache, collect uncached queries
  const results: RagInsight[] = [];
  const uncachedQueries: { index: number; query: string }[] = [];

  for (let i = 0; i < queries.length; i++) {
    const key = cacheKey(league, homeShort, awayShort, queries[i]);
    const cached = cache.get(key);
    if (cached && cached.expiresAt > now) {
      results[i] = { ...cached.result, cached: true };
    } else {
      uncachedQueries.push({ index: i, query: queries[i] });
    }
  }

  // Fetch uncached queries in parallel
  if (uncachedQueries.length > 0) {
    const fetches = uncachedQueries.map(async ({ index, query }) => {
      const chunks = await fetchRag(query, k);
      const insight: RagInsight = {
        query,
        topChunks: chunks,
        applicableTheory: summarizeChunks(chunks),
        cached: false,
      };

      // Store in cache
      const key = cacheKey(league, homeShort, awayShort, query);
      cache.set(key, { result: insight, expiresAt: now + CACHE_TTL_MS });

      results[index] = insight;
    });

    await Promise.allSettled(fetches);
  }

  // Fill any gaps with empty insights (in case of failures)
  for (let i = 0; i < queries.length; i++) {
    if (!results[i]) {
      results[i] = { query: queries[i], topChunks: [], applicableTheory: '', cached: false };
    }
  }

  return results;
}

/**
 * Compute a RAG relevance score (0-1) from insights.
 * Higher scores mean the knowledge base has strong, relevant theory for this matchup.
 */
export function computeRagRelevanceScore(insights: RagInsight[]): number {
  if (insights.length === 0) return 0.5; // neutral

  // Average the top chunk scores across all queries
  let totalScore = 0;
  let count = 0;
  for (const insight of insights) {
    for (const chunk of insight.topChunks) {
      totalScore += chunk.score;
      count++;
    }
  }

  if (count === 0) return 0.5;

  const avgScore = totalScore / count;
  // RAG scores typically range 5-15 for relevant results (BM25 + vector hybrid)
  // Map: <5 = low relevance (0.50), 10 = good (0.60), 15+ = excellent (0.70)
  const normalized = 0.50 + Math.min((avgScore - 5) * 0.02, 0.20);
  return Math.max(0.50, Math.min(0.70, normalized));
}

/**
 * Evict expired cache entries. Call periodically.
 */
export function evictExpiredCache(): number {
  const now = Date.now();
  let evicted = 0;
  for (const [key, entry] of cache) {
    if (entry.expiresAt <= now) {
      cache.delete(key);
      evicted++;
    }
  }
  return evicted;
}

/**
 * Get cache stats for monitoring.
 */
export function getRagCacheStats(): { size: number; oldestAge: number } {
  const now = Date.now();
  let oldestAge = 0;
  for (const entry of cache.values()) {
    const age = now - (entry.expiresAt - CACHE_TTL_MS);
    if (age > oldestAge) oldestAge = age;
  }
  return { size: cache.size, oldestAge: Math.round(oldestAge / 1000) };
}
