/**
 * RuVector RAG Decomposition Learning - Phase 4 Task 4.2
 *
 * RAG (Retrieval-Augmented Generation) query system for finding similar
 * prior decompositions. Uses embedding-based similarity search to suggest
 * baseline decompositions for new tasks.
 *
 * Key Features:
 * - Semantic similarity search (<500ms latency SLA)
 * - Top-K prior decompositions with quality scores
 * - Adaptive prompting for decomposers (use successful priors as baseline)
 * - RAG recall tracking (relevance metrics)
 *
 * Integration Points:
 * - Called BEFORE Phase 2 decomposition (in cfn-coordinator.ts)
 * - Feeds prior context to cfn-thinking-decomposer.ts and other decomposers
 * - Tracks whether RAG suggestions improve decomposition quality
 *
 * Reference: Phase 4 RuVector Learning Systems Integration (Task 4.2)
 */

import { getCollection, COLLECTIONS } from './ruvector-init.js';
import type { DecompositionHistoryEntry } from './ruvector-schemas.js';

// =============================================
// Types
// =============================================

/**
 * RAG query result for similar decompositions
 */
export interface SimilarDecomposition {
  taskId: string;
  taskDescription: string;
  similarity: number; // 0.0-1.0 (cosine similarity)
  decompositionApproach: string;
  microTaskCount: number;
  executionPhases: number;
  gateCheckScore: number;
  qualityScore: number; // Composite: (gateCheckScore + successRate) / 2
  executionTimeMs: number;
  securityRiskLevel: string;
  performanceGrade: string;
  successRate: number;
  timesUsed: number;
}

/**
 * RAG query options
 */
export interface RagQueryOptions {
  topK?: number; // Number of similar decompositions to return (default: 3)
  minSimilarity?: number; // Minimum similarity threshold (default: 0.75)
  minQualityScore?: number; // Minimum quality score filter (default: 0.80)
  onlySuccessful?: boolean; // Only return PROCEED decisions (default: true)
}

/**
 * RAG query result with metadata
 */
export interface RagQueryResult {
  query: string;
  results: SimilarDecomposition[];
  totalFound: number;
  avgSimilarity: number;
  avgQualityScore: number;
  queryTimeMs: number;
  hasHighConfidencePrior: boolean; // At least one result with qualityScore > 0.90
}

// =============================================
// Task 4.2.1: RAG Similarity Search
// =============================================

/**
 * Find similar prior decompositions using RAG query
 *
 * Searches decomposition_history collection for semantically similar tasks.
 * Returns top-K results with similarity scores, quality metrics, and timing.
 *
 * @param taskDescription - New task description to match
 * @param options - Query options (topK, filters, thresholds)
 * @returns Promise<RagQueryResult> - Similar decompositions with metadata
 *
 * @example
 * const ragResult = await findSimilarDecompositions(
 *   "Create a REST API endpoint for user authentication",
 *   { topK: 3, minSimilarity: 0.75, onlySuccessful: true }
 * );
 *
 * if (ragResult.hasHighConfidencePrior) {
 *   console.log(`Found high-confidence prior: ${ragResult.results[0].taskId}`);
 *   // Feed to decomposer as baseline
 * }
 */
export async function findSimilarDecompositions(
  taskDescription: string,
  options: RagQueryOptions = {}
): Promise<RagQueryResult> {
  const startTime = Date.now();

  const {
    topK = 3,
    minSimilarity = 0.75,
    minQualityScore = 0.80,
    onlySuccessful = true,
  } = options;

  try {
    const collection = getCollection(COLLECTIONS.DECOMPOSITION_HISTORY);

    // Generate embedding for new task (RuVector handles this via text field)
    // For now, use placeholder embedding (real embedding via Cerebras in production)
    const queryEmbedding = await generateEmbedding(taskDescription);

    // Query RuVector with filters
    const searchResults = await collection.search({
      vector: queryEmbedding,
      k: topK * 2, // Fetch more to allow for filtering
      filter: {
        // Filter by quality and success
        gateCheckScore: { $gte: minQualityScore },
        ...(onlySuccessful && { finalDecision: 'PROCEED' }),
      },
    });

    // Convert to SimilarDecomposition format
    const results: SimilarDecomposition[] = searchResults
      .filter((r) => r.score >= minSimilarity)
      .slice(0, topK)
      .map((r) => ({
        taskId: r.metadata.taskId,
        taskDescription: r.metadata.originalTask,
        similarity: r.score,
        decompositionApproach: r.metadata.decompositionApproach,
        microTaskCount: r.metadata.microTaskCount,
        executionPhases: r.metadata.executionPhases,
        gateCheckScore: r.metadata.gateCheckScore,
        qualityScore: calculateQualityScore(r.metadata),
        executionTimeMs: r.metadata.totalTimeMs,
        securityRiskLevel: r.metadata.securityRiskLevel,
        performanceGrade: r.metadata.performanceGrade,
        successRate: r.metadata.successRate,
        timesUsed: r.metadata.timesUsed,
      }));

    const queryTimeMs = Date.now() - startTime;

    // Enforce SLA: <500ms
    if (queryTimeMs > 500) {
      console.warn(
        `[rag] ⚠️ RAG query took ${queryTimeMs}ms (>500ms SLA violation)`
      );
    }

    // Calculate aggregate metrics
    const avgSimilarity =
      results.length > 0
        ? results.reduce((sum, r) => sum + r.similarity, 0) / results.length
        : 0;
    const avgQualityScore =
      results.length > 0
        ? results.reduce((sum, r) => sum + r.qualityScore, 0) / results.length
        : 0;
    const hasHighConfidencePrior = results.some((r) => r.qualityScore > 0.9);

    console.log(
      `[rag] ✓ Found ${results.length} similar decompositions (query: ${queryTimeMs}ms)`
    );
    if (hasHighConfidencePrior) {
      console.log(
        `[rag]   High-confidence prior available: ${results[0].taskId} (quality: ${results[0].qualityScore.toFixed(2)})`
      );
    }

    return {
      query: taskDescription,
      results,
      totalFound: results.length,
      avgSimilarity,
      avgQualityScore,
      queryTimeMs,
      hasHighConfidencePrior,
    };
  } catch (error) {
    console.error(
      `[rag] Failed to query similar decompositions: ${error instanceof Error ? error.message : String(error)}`
    );

    // Return empty result on error (graceful degradation)
    return {
      query: taskDescription,
      results: [],
      totalFound: 0,
      avgSimilarity: 0,
      avgQualityScore: 0,
      queryTimeMs: Date.now() - startTime,
      hasHighConfidencePrior: false,
    };
  }
}

// =============================================
// Task 4.2.2: Adaptive Prompting with RAG
// =============================================

/**
 * Generate adaptive prompt for decomposer with RAG context
 *
 * If high-confidence prior decomposition exists, generate a prompt that
 * instructs the decomposer to use it as a baseline and refine for new context.
 *
 * @param taskDescription - New task description
 * @param ragResult - RAG query result with similar decompositions
 * @returns string - Enhanced prompt with RAG context
 *
 * @example
 * const ragResult = await findSimilarDecompositions(taskDescription);
 * const enhancedPrompt = generateAdaptivePrompt(taskDescription, ragResult);
 * // Feed to cfn-thinking-decomposer.ts
 */
export function generateAdaptivePrompt(
  taskDescription: string,
  ragResult: RagQueryResult
): string {
  // Base prompt (no RAG context)
  if (!ragResult.hasHighConfidencePrior || ragResult.results.length === 0) {
    return taskDescription; // Use original prompt
  }

  // Enhanced prompt with RAG baseline
  const priorDecomposition = ragResult.results[0];

  const adaptivePrompt = `
# Task Description
${taskDescription}

# Prior Successful Decomposition (Baseline)
A similar task was successfully completed with the following approach:

**Task**: ${priorDecomposition.taskDescription}
**Approach**: ${priorDecomposition.decompositionApproach}
**Micro-tasks**: ${priorDecomposition.microTaskCount}
**Execution Phases**: ${priorDecomposition.executionPhases}
**Quality Score**: ${priorDecomposition.qualityScore.toFixed(2)} (gate: ${priorDecomposition.gateCheckScore.toFixed(2)})
**Similarity**: ${(priorDecomposition.similarity * 100).toFixed(0)}%

# Adaptive Instructions
Given the successful prior decomposition above:
1. Use it as a baseline for the new task
2. Identify differences in requirements, constraints, or context
3. Refine the approach to address new task specifics
4. Maintain the same decomposition quality (micro-task granularity, phase structure)
5. If the new task is simpler, reduce scope; if more complex, add phases

# Additional Context
- Security Risk: ${priorDecomposition.securityRiskLevel}
- Performance: ${priorDecomposition.performanceGrade}
- Execution Time: ${(priorDecomposition.executionTimeMs / 1000).toFixed(1)}s
- Times Reused: ${priorDecomposition.timesUsed} (success rate: ${(priorDecomposition.successRate * 100).toFixed(0)}%)
`;

  return adaptivePrompt.trim();
}

// =============================================
// Task 4.2.3: RAG Recall Tracking
// =============================================

/**
 * Track RAG recall: How often RAG results were relevant to task
 *
 * Called after decomposition completes. Compares final decomposition quality
 * to RAG baseline suggestion. Updates RuVector metadata for learning feedback.
 *
 * @param taskId - Task ID of completed decomposition
 * @param ragResult - RAG query result used for baseline
 * @param finalGateCheckScore - Final gate check score after decomposition
 * @returns Promise<void> - Fire-and-forget
 */
export async function trackRagRecall(
  taskId: string,
  ragResult: RagQueryResult,
  finalGateCheckScore: number
): Promise<void> {
  try {
    if (!ragResult.hasHighConfidencePrior) {
      return; // No baseline to track
    }

    const priorDecomposition = ragResult.results[0];

    // Did RAG improve decomposition quality?
    const improvementOverBaseline =
      finalGateCheckScore - priorDecomposition.gateCheckScore;

    // Update prior decomposition metadata: increment timesUsed, update successRate
    const collection = getCollection(COLLECTIONS.DECOMPOSITION_HISTORY);

    const currentTimesUsed = priorDecomposition.timesUsed;
    const currentSuccessRate = priorDecomposition.successRate;

    const newTimesUsed = currentTimesUsed + 1;
    const newSuccessRate =
      (currentSuccessRate * currentTimesUsed + (finalGateCheckScore >= 0.95 ? 1 : 0)) /
      newTimesUsed;

    await collection.update(priorDecomposition.taskId, {
      metadata: {
        timesUsed: newTimesUsed,
        successRate: newSuccessRate,
        lastUsed: Date.now(),
      },
    });

    console.log(
      `[rag] ✓ RAG recall tracked: ${priorDecomposition.taskId} (improvement: ${improvementOverBaseline >= 0 ? '+' : ''}${improvementOverBaseline.toFixed(2)})`
    );
  } catch (error) {
    console.warn(
      `[rag] Failed to track RAG recall: ${error instanceof Error ? error.message : String(error)}`
    );
  }
}

// =============================================
// Helper Functions
// =============================================

/**
 * Calculate composite quality score from metadata
 */
function calculateQualityScore(metadata: any): number {
  const gateScore = metadata.gateCheckScore ?? 0;
  const successRate = metadata.successRate ?? 0;
  return (gateScore + successRate) / 2;
}

/**
 * Generate embedding for task description
 *
 * In production: Use Cerebras embeddings via cerebras-provider.ts
 * For now: Mock with random embedding for testing
 *
 * @param text - Text to embed
 * @returns Promise<Float32Array> - 1536-dimensional embedding
 */
async function generateEmbedding(text: string): Promise<Float32Array> {
  // TODO: Integrate with Cerebras embeddings (Task 4.2 enhancement)
  // For now, return mock embedding based on text hash
  const hash = simpleHash(text);
  const embedding = new Float32Array(1536);

  for (let i = 0; i < 1536; i++) {
    embedding[i] = Math.sin((hash + i) * 0.01); // Deterministic mock
  }

  return embedding;
}

/**
 * Simple string hash for mock embeddings
 */
function simpleHash(str: string): number {
  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 32-bit integer
  }
  return Math.abs(hash);
}
