'use strict';

let DEFAULT_TIER_MODELS;
try {
  ({ DEFAULT_TIER_MODELS } = require('./tier-selector'));
} catch (_) {
  // Inline fallback when tier-selector hasn't been created yet
  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' },
  };
}

let createClient;
try {
  ({ createClient } = require('../llm/client'));
} catch (_) {
  createClient = () => { throw new Error('LLM client not available'); };
}

/**
 * Whether self-critique should run for this model tier.
 * Fast tier skips critique to preserve latency budget.
 * @param {string} modelTier - 'fast', 'balanced', or 'quality'
 * @returns {boolean}
 */
function shouldSelfCritique(modelTier) {
  return modelTier !== 'fast';
}

/**
 * Build the prompt sent to the critic model.
 * @param {string} userMessage - Original user question
 * @param {string} workerResponse - The worker's response text
 * @returns {string}
 */
function buildCritiquePrompt(userMessage, workerResponse) {
  return `Review the following response for: factual errors, logical gaps, missing nuance, unsupported claims, and tone issues.
Be specific about any issues found. If the response is solid, say exactly "No issues found."

Original question: ${userMessage}

Response to review:
${workerResponse}`;
}

/**
 * Parse the critic's response to determine if issues were found.
 * @param {string} critiqueText
 * @returns {{ hasIssues: boolean, issues: string|null }}
 */
function parseCritiqueResult(critiqueText) {
  if (/no issues found/i.test(critiqueText)) {
    return { hasIssues: false, issues: null };
  }
  return { hasIssues: true, issues: critiqueText.trim() };
}

/**
 * Select a critic model, preferring cross-provider diversity.
 * Critics always use balanced-tier models.
 * @param {string} workerProviderType - Provider that produced the worker response
 * @param {Array<{providerType: string}>} availableProviders
 * @param {string} modelTier - The worker's model tier (unused for critic model selection, but kept for API consistency)
 * @returns {{ providerType: string, model: string }}
 */
function selectCriticModel(workerProviderType, availableProviders, modelTier) {
  // Prefer a different provider for diversity
  const otherProvider = availableProviders.find(p => p.providerType !== workerProviderType);
  if (otherProvider) {
    const providerType = otherProvider.providerType;
    const models = DEFAULT_TIER_MODELS[providerType];
    const model = models ? models.balanced : DEFAULT_TIER_MODELS.anthropic.balanced;
    return { providerType, model };
  }

  // Fall back to same provider with balanced tier model
  const providerType = workerProviderType;
  const models = DEFAULT_TIER_MODELS[providerType];
  const model = models ? models.balanced : DEFAULT_TIER_MODELS.anthropic.balanced;
  return { providerType, model };
}

/**
 * Run self-critique on a worker response. If issues are found and the response
 * hasn't been refined yet, the worker gets one refinement pass.
 *
 * @param {object} opts
 * @param {string} opts.userMessage - Original user question
 * @param {string} opts.workerResponse - The worker's response text
 * @param {string} opts.workerModel - Model that produced the response
 * @param {string} opts.workerProvider - Provider type that produced it
 * @param {string} opts.modelTier - 'fast', 'balanced', or 'quality'
 * @param {Array<{providerType: string}>} opts.availableProviders
 * @param {boolean} opts.refinedAlready - True if a prior step already refined
 * @param {function} [opts.clientFactory] - (providerType, config) => client
 * @returns {Promise<{outcome: string, refinedResponse: string|null, latencyMs: number, issues: string|null, error?: string}>}
 */
async function selfCritique(opts) {
  const {
    userMessage,
    workerResponse,
    workerModel,
    workerProvider,
    modelTier,
    availableProviders,
    refinedAlready,
    clientFactory = createClient,
  } = opts;

  // Step 1: Check if critique should run
  if (!shouldSelfCritique(modelTier)) {
    return { outcome: 'skipped', refinedResponse: null, latencyMs: 0, issues: null };
  }

  const startTime = Date.now();

  try {
    // Step 2: Select critic model
    const critic = selectCriticModel(workerProvider, availableProviders, modelTier);

    // Step 3: Create critic client and call
    const criticClient = clientFactory(critic.providerType, { model: critic.model });
    const critiquePrompt = buildCritiquePrompt(userMessage, workerResponse);
    const critiqueResponse = await criticClient.chat({
      model: critic.model,
      messages: [{ role: 'user', content: critiquePrompt }],
      maxTokens: 1024,
      temperature: 0.3,
    });

    // Step 4: Parse critique
    const critiqueResult = parseCritiqueResult(critiqueResponse.content);

    // Step 5: No issues
    if (!critiqueResult.hasIssues) {
      return { outcome: 'no-issues', refinedResponse: null, latencyMs: Date.now() - startTime, issues: null };
    }

    // Step 6: Issues but already refined
    if (refinedAlready) {
      return { outcome: 'skipped-already-refined', refinedResponse: null, latencyMs: Date.now() - startTime, issues: critiqueResult.issues };
    }

    // Step 7: Issues — do one refinement pass with the worker model
    const refinementPrompt = `A reviewer found these issues with your response:
${critiqueResult.issues}

Original question: ${userMessage}
Your original response: ${workerResponse}

Please provide an improved response that addresses the reviewer's feedback.`;

    const workerClient = clientFactory(workerProvider, { model: workerModel });
    const refinedResponse = await workerClient.chat({
      model: workerModel,
      messages: [{ role: 'user', content: refinementPrompt }],
      maxTokens: 2048,
      temperature: 0.3,
    });

    return {
      outcome: 'refined',
      refinedResponse: refinedResponse.content,
      latencyMs: Date.now() - startTime,
      issues: critiqueResult.issues,
    };
  } catch (err) {
    // Step 8: Error handling
    return {
      outcome: 'error',
      refinedResponse: null,
      latencyMs: Date.now() - startTime,
      issues: null,
      error: err.message,
    };
  }
}

module.exports = {
  shouldSelfCritique,
  buildCritiquePrompt,
  parseCritiqueResult,
  selectCriticModel,
  selfCritique,
};
