'use strict';

/**
 * Adaptive learning loop for automatic model preference updates.
 * Analyzes evaluation history and updates model defaults when
 * a consistently better model is found.
 */

const { computeScore } = require('./scorecard');

const TASK_TYPES = ['chat', 'coding', 'planning', 'qa', 'extraction', 'reflection', 'initiative', 'skill',
  'coding:planning', 'coding:exploration', 'coding:generation', 'coding:debugging', 'coding:refactoring', 'coding:testing', 'coding:review'];

// Thresholds for auto-applying changes
const MIN_EVALS_AUTO = 20;       // Minimum evaluations to auto-apply
const MIN_EVALS_RECOMMEND = 5;   // Minimum evaluations to even recommend
const MIN_IMPROVEMENT = 0.10;    // 10% improvement required for auto-apply
const CONSISTENCY_DAYS = 7;      // Must be better over 7-day window too

/**
 * Analyze model evaluation history and update defaults.
 * Should be called periodically (e.g., daily or after N evaluations).
 * @param {Object} deps - { brain }
 * @returns {{ updates: Array, recommendations: Array }}
 */
async function updateModelPreferences(deps = {}) {
  const { brain } = deps;
  if (!brain) return { updates: [], recommendations: [] };

  const updates = [];
  const recommendations = [];

  // Get all registered models
  const allModels = brain.listAllModels();
  if (!allModels || allModels.length === 0) {
    return { updates: [], recommendations: [] };
  }

  for (const taskType of TASK_TYPES) {
    const currentDefault = brain.getModelDefault(taskType);
    const currentModelId = currentDefault?.model_registry_id || null;

    // Score all models for this task type over the last 30 days
    const scored = [];
    for (const model of allModels) {
      const modelId = model.id;
      const stats30 = brain.getModelScorecardForTask
        ? brain.getModelScorecardForTask(modelId, taskType, 30)
        : brain.getModelScorecard(modelId, 30);

      if (!stats30 || stats30.total_evals === 0) continue;

      let { compositeScore } = computeScore(stats30, taskType);

      // Blend benchmark scores (20%) with quorum scorecard (80%) if available
      try {
        if (brain.getBenchmarkLeaderboard) {
          const benchLb = brain.getBenchmarkLeaderboard({ days: 30 });
          const benchEntry = benchLb.find(b => b.model === (model.model_id || modelId));
          if (benchEntry && benchEntry.total_evals >= 5) {
            compositeScore = compositeScore * 0.8 + benchEntry.avg_score * 0.2;
          }
        }
      } catch {}

      scored.push({
        modelId,
        displayName: model.display_name || model.model_id || modelId,
        stats30,
        compositeScore,
        evalCount: stats30.total_evals,
      });
    }

    if (scored.length === 0) continue;

    // Sort by composite score descending
    scored.sort((a, b) => b.compositeScore - a.compositeScore);
    const best = scored[0];

    // If best model is already the default, skip
    if (best.modelId === currentModelId) continue;

    // Find current default's score (if it exists in scored list)
    const currentScored = currentModelId
      ? scored.find(s => s.modelId === currentModelId)
      : null;
    const currentScore = currentScored?.compositeScore ?? 0;

    // Calculate improvement percentage
    const improvement = currentScore > 0
      ? (best.compositeScore - currentScore) / currentScore
      : 1.0; // If no current default, any model is infinitely better

    const improvementPct = Math.round(improvement * 100);

    // Check consistency: also better over last 7 days
    let consistentOver7Days = false;
    if (brain.getModelScorecardForTask) {
      const bestStats7 = brain.getModelScorecardForTask(best.modelId, taskType, CONSISTENCY_DAYS);
      const currentStats7 = currentModelId
        ? brain.getModelScorecardForTask(currentModelId, taskType, CONSISTENCY_DAYS)
        : null;

      if (bestStats7 && bestStats7.total_evals > 0) {
        const bestScore7 = computeScore(bestStats7, taskType).compositeScore;
        const currentScore7 = currentStats7 && currentStats7.total_evals > 0
          ? computeScore(currentStats7, taskType).compositeScore
          : 0;
        consistentOver7Days = bestScore7 > currentScore7;
      }
    } else {
      // If no task-specific scorecard, assume consistent if 30-day data looks good
      consistentOver7Days = true;
    }

    // Decision: auto-apply or recommend?
    const canAutoApply =
      best.evalCount >= MIN_EVALS_AUTO &&
      improvement >= MIN_IMPROVEMENT &&
      consistentOver7Days;

    if (canAutoApply) {
      // Auto-apply the change
      const existingDefault = brain.getModelDefault(taskType);
      const strategy = existingDefault?.strategy || 'single';
      const quorumSize = existingDefault?.quorum_size || 1;
      brain.setModelDefault(taskType, best.modelId, strategy, quorumSize);
      updates.push({
        taskType,
        oldModel: currentModelId || '(none)',
        newModel: best.modelId,
        reason: `${best.displayName} scores ${improvementPct}% better with ${best.evalCount} evaluations`,
        autoApplied: true,
      });
    } else if (best.evalCount >= MIN_EVALS_RECOMMEND && improvement > 0) {
      // Create a recommendation but don't auto-apply
      let reason;
      if (best.evalCount < MIN_EVALS_AUTO) {
        reason = `Needs more data (only ${best.evalCount} evals, need ${MIN_EVALS_AUTO})`;
      } else if (improvement < MIN_IMPROVEMENT) {
        reason = `Improvement only ${improvementPct}%, need ${Math.round(MIN_IMPROVEMENT * 100)}%`;
      } else if (!consistentOver7Days) {
        reason = `Not consistently better over last ${CONSISTENCY_DAYS} days`;
      } else {
        reason = `Marginal improvement of ${improvementPct}%`;
      }

      recommendations.push({
        taskType,
        currentModel: currentModelId || '(none)',
        suggestedModel: best.modelId,
        improvement: `${improvementPct}%`,
        reason,
      });
    }
  }

  return { updates, recommendations };
}

/**
 * Get a summary of current model performance for dashboard display.
 * @param {Object} deps - { brain }
 * @returns {Object} - Per-task-type performance summary
 */
function getPerformanceSummary(deps = {}) {
  const { brain } = deps;
  if (!brain) return {};

  const allModels = brain.listAllModels();
  if (!allModels || allModels.length === 0) return {};

  const summary = {};

  for (const taskType of TASK_TYPES) {
    const currentDefault = brain.getModelDefault(taskType);
    const models = [];

    for (const model of allModels) {
      const modelId = model.id;
      const stats = brain.getModelScorecardForTask
        ? brain.getModelScorecardForTask(modelId, taskType, 30)
        : brain.getModelScorecard(modelId, 30);

      if (!stats || stats.total_evals === 0) continue;

      const { compositeScore, breakdown } = computeScore(stats, taskType);
      models.push({
        modelId,
        displayName: model.display_name || model.model_id || modelId,
        compositeScore,
        breakdown,
        evalCount: stats.total_evals,
        avgQuality: stats.avg_quality,
        avgLatencyMs: stats.avg_latency_ms,
        isDefault: currentDefault?.model_registry_id === modelId,
      });
    }

    models.sort((a, b) => b.compositeScore - a.compositeScore);

    summary[taskType] = {
      currentDefault: currentDefault?.model_registry_id || null,
      models,
      totalEvals: models.reduce((sum, m) => sum + m.evalCount, 0),
    };
  }

  return summary;
}

module.exports = {
  updateModelPreferences,
  getPerformanceSummary,
  TASK_TYPES,
  MIN_EVALS_AUTO,
  MIN_EVALS_RECOMMEND,
  MIN_IMPROVEMENT,
  CONSISTENCY_DAYS,
};
