'use strict';

const DEFAULT_TIER_MODELS = {
  anthropic: { fast: 'claude-haiku-4-5-20251001', balanced: 'claude-sonnet-4-6', quality: 'claude-opus-4-7' },
  openai: { fast: 'gpt-5.4-mini', balanced: 'gpt-5.4', quality: 'gpt-5.5' },
  google: { fast: 'gemini-2.5-flash', balanced: 'gemini-2.5-pro', quality: 'gemini-2.5-pro' },
  deepseek: { fast: 'deepseek-v4-flash', balanced: 'deepseek-v4-pro', quality: 'deepseek-v4-pro' },
  moonshot: { fast: 'kimi-k2-turbo-preview', balanced: 'kimi-k2.6', quality: 'kimi-k2.6' },
  ollama: { fast: 'gemma4:e4b', balanced: 'gemma4:26b', quality: 'gemma4:26b' },
};

const COMPLEXITY_TO_TIER = {
  simple: 'fast',
  moderate: 'balanced',
  complex: 'quality',
  critical: 'quality',
};

const TIER_ENV_VARS = {
  fast: 'WALLE_MODEL_FAST',
  balanced: 'WALLE_MODEL_BALANCED',
  quality: 'WALLE_MODEL_QUALITY',
};

/**
 * Map a complexity tier string to a model tier string.
 * @param {string} complexityTier - 'simple', 'moderate', 'complex', or 'critical'
 * @returns {string} 'fast', 'balanced', or 'quality'
 */
function complexityToModelTier(complexityTier) {
  return COMPLEXITY_TO_TIER[complexityTier] || 'balanced';
}

/**
 * Check if a complexity tier is critical.
 * @param {string} complexityTier
 * @returns {boolean}
 */
function isCritical(complexityTier) {
  return complexityTier === 'critical';
}

/**
 * Select a model for a given tier and provider.
 * @param {string} modelTier - 'fast', 'balanced', or 'quality'
 * @param {string} providerType - 'anthropic', 'openai', 'google', 'ollama'
 * @param {object} [opts={}]
 * @param {object} [opts.brain] - Optional brain instance for scorecard lookup
 * @param {string} [opts.taskType] - Optional task type for scorecard weighting
 * @returns {string} Model identifier
 */
function selectModelForTier(modelTier, providerType, opts = {}) {
  // 1. Check env var override
  const envVar = TIER_ENV_VARS[modelTier];
  if (envVar && process.env[envVar] && _modelMatchesProvider(process.env[envVar], providerType)) {
    return process.env[envVar];
  }

  // 2. Check brain scorecard for best model in this tier
  if (opts.brain && opts.taskType) {
    try {
      const scorecardModel = _getScorecardModel(opts.brain, providerType, modelTier, opts.taskType);
      if (scorecardModel) return scorecardModel;
    } catch (_) {
      // Scorecard lookup failed, fall through to defaults
    }
  }

  // 3. Use default for this provider
  const providerDefaults = DEFAULT_TIER_MODELS[providerType];
  if (providerDefaults && providerDefaults[modelTier]) {
    return providerDefaults[modelTier];
  }

  // 4. Fallback to anthropic
  return DEFAULT_TIER_MODELS.anthropic[modelTier];
}

function _modelMatchesProvider(model, providerType) {
  try {
    const registry = require('../llm/registry');
    registry.ensureBootstrapped();
    return registry.modelMatchesProvider(model, providerType);
  } catch {
    return true;
  }
}

/**
 * Select the best worker model given complexity assessment and available providers.
 * @param {object} complexityResult - Output from assessComplexity() with .tier
 * @param {Array<{providerType: string}>} availableProviders
 * @param {object} [brain] - Optional brain instance for scorecard lookup
 * @returns {{ model: string, providerType: string, modelTier: string, isCritical: boolean }}
 */
function selectWorkerModel(complexityResult, availableProviders, brain) {
  const modelTier = complexityToModelTier(complexityResult.tier);
  const critical = isCritical(complexityResult.tier);
  const taskType = complexityResult.type || undefined;

  let bestModel = null;
  let bestProvider = null;
  let bestScore = -1;

  for (const provider of availableProviders) {
    const model = selectModelForTier(modelTier, provider.providerType, { brain, taskType });

    // If brain has scorecard data, score this candidate
    if (brain && taskType) {
      try {
        const score = _getModelScore(brain, provider.providerType, model, taskType);
        if (score > bestScore) {
          bestScore = score;
          bestModel = model;
          bestProvider = provider.providerType;
        }
        continue;
      } catch (_) {
        // No scorecard data, fall through
      }
    }

    // Without scorecard, pick first available
    if (!bestModel) {
      bestModel = model;
      bestProvider = provider.providerType;
    }
  }

  // Fallback if no providers matched
  if (!bestModel) {
    bestProvider = 'anthropic';
    bestModel = DEFAULT_TIER_MODELS.anthropic[modelTier];
  }

  return {
    model: bestModel,
    providerType: bestProvider,
    modelTier,
    isCritical: critical,
  };
}

/**
 * Try to get the best model from scorecard data using brain.getModelTaskScores().
 * @private
 */
function _getScorecardModel(brain, providerType, modelTier, taskType) {
  if (typeof brain.getModelTaskScores !== 'function') return null;
  const scores = brain.getModelTaskScores(taskType);
  if (!scores || scores.length === 0) return null;
  // Filter to models from the right provider, sort by avg_score desc
  const matching = scores
    .filter(s => s.provider === providerType && s.total_evals >= 10)
    .sort((a, b) => (b.avg_score || 0) - (a.avg_score || 0));
  return matching.length > 0 ? matching[0].model : null;
}

/**
 * Get composite score for a specific model from brain.getModelTaskScores().
 * @private
 */
function _getModelScore(brain, providerType, model, taskType) {
  if (typeof brain.getModelTaskScores !== 'function') return 0;
  const scores = brain.getModelTaskScores(taskType);
  if (!scores || scores.length === 0) return 0;
  const match = scores.find(s => s.model === model && s.provider === providerType);
  return match ? (match.avg_score || 0) : 0;
}

module.exports = {
  DEFAULT_TIER_MODELS,
  COMPLEXITY_TO_TIER,
  complexityToModelTier,
  isCritical,
  selectModelForTier,
  selectWorkerModel,
};
