// ─── Discipline Math Utilities ────────────────────────────────────────────────
// Continuous scaling formulas for Active Disciplines

import type { DisciplineState, DisciplineDefinition, DisciplinePerk } from '../types/disciplines';

/**
 * Calculate continuous stat bonus from discipline XP
 * StatBonus = BaseValue * (XP / ScalingFactor)^0.65
 */
export function calculateStatBonus(
  baseValue: number,
  xp: number,
  scalingFactor: number
): number {
  if (xp <= 0) return 0;
  const ratio = xp / scalingFactor;
  const power = Math.pow(ratio, 0.65);
  return baseValue * power;
}

/**
 * Calculate dynamic mana drain per tick
 * ManaDrainPerTick = BaseDrain * (1 + (XP / DifficultyFactor)^0.4)
 */
export function calculateManaDrain(
  baseDrain: number,
  xp: number,
  difficultyFactor: number
): number {
  if (xp <= 0) return baseDrain;
  const ratio = xp / difficultyFactor;
  const power = Math.pow(ratio, 0.4);
  return baseDrain * (1 + power);
}

/**
 * Calculate infinite perk tier
 * PerkTier = Math.max(0, Math.floor((XP - Threshold) / Interval) + 1)
 */
export function calculatePerkTier(
  xp: number,
  threshold: number,
  interval: number
): number {
  if (xp < threshold) return 0;
  const excess = xp - threshold;
  return Math.max(0, Math.floor(excess / interval) + 1);
}

/**
 * Check if discipline can be activated (has required mana type)
 */
export function canActivateDiscipline(
  discipline: DisciplineDefinition,
  gameState: { elements?: Record<string, any> }
): boolean {
  if (discipline.manaType === 'raw') return true;
  const element = gameState.elements?.[discipline.manaType];
  return element && element.unlocked;
}

/**
 * Check if discipline can proceed.
 * Under the new unified conversion model, disciplines do not drain mana from pools.
 * This check now only verifies that prerequisites are structurally met.
 */
export function canProceedDiscipline(
  discipline: DisciplineDefinition,
  disciplineState: DisciplineState | undefined,
  gameState?: { elements?: Record<string, any>; rawMana?: number }
): boolean {
  // New disciplines can always be activated (prerequisites checked separately)
  if (!disciplineState) return true;

  // If no game state provided, allow activation (optimistic)
  if (!gameState) return true;

  // For disciplines with source mana types, verify they exist in game state
  // (actual unlock check happens in activate() separately)
  if (discipline.manaType === 'raw') return true;

  const element = gameState.elements?.[discipline.manaType];
  return !!element;
}

// ─── Known mana type names for display ────────────────────────────────────────
const MANA_TYPE_NAMES: Record<string, string> = {
  raw: 'raw',
  fire: 'fire',
  water: 'water',
  air: 'air',
  earth: 'earth',
  light: 'light',
  dark: 'dark',
  death: 'death',
  transference: 'transference',
  metal: 'metal',
  sand: 'sand',
  lightning: 'lightning',
  frost: 'frost',
  blackflame: 'blackflame',
  radiantflames: 'radiantflames',
  miasma: 'miasma',
  shadowglass: 'shadowglass',
  crystal: 'crystal',
  stellar: 'stellar',
  void: 'void',
  soul: 'soul',
  time: 'time',
  plasma: 'plasma',
};

/**
 * Check if a discipline's prerequisites are met.
 * For disciplines with sourceManaTypes (conversion disciplines), requires are
 * mana type IDs that must be unlocked (e.g. fire, earth).
 * Returns { canProceed: boolean, missingPrereqs: string[] }
 * where missingPrereqs is a list of human-readable prerequisite descriptions.
 */
export function checkDisciplinePrerequisites(
  discipline: DisciplineDefinition,
  allDisciplines: Record<string, DisciplineState>,
  allDefinitions: DisciplineDefinition[],
  elements?: Record<string, { unlocked: boolean }>,
  signedPacts?: number[],
): { canProceed: boolean; missingPrereqs: string[] } {
  if (!discipline.requires || discipline.requires.length === 0) {
    return { canProceed: true, missingPrereqs: [] };
  }

  const missingPrereqs: string[] = [];

  for (const reqId of discipline.requires) {
    // Special case: 'signed_pact' requires at least one guardian pact
    if (reqId === 'signed_pact') {
      if (!signedPacts || signedPacts.length === 0) {
        missingPrereqs.push('Signed guardian pact');
      }
      continue;
    }

    // Check if this is a discipline prerequisite (exists in definitions)
    const reqDef = allDefinitions.find((d) => d.id === reqId);
    if (reqDef) {
      const reqState = allDisciplines[reqId];
      if (!reqState || reqState.xp <= 0) {
        missingPrereqs.push(reqDef?.name ?? reqId);
      }
    } else {
      // Treat as mana type requirement — verify against player's unlocked elements
      const typeName = MANA_TYPE_NAMES[reqId];
      if (typeName) {
        const elementState = elements?.[reqId];
        if (!elementState || !elementState.unlocked) {
          missingPrereqs.push(`${typeName} mana`);
        }
      } else {
        missingPrereqs.push(reqId);
      }
    }
  }

  return { canProceed: missingPrereqs.length === 0, missingPrereqs };
}

/**
 * Get unlocked perks for a discipline
 */
export function getUnlockedPerks(
  discipline: DisciplineDefinition,
  xp: number
): DisciplinePerk[] {
  return discipline.perks.filter(perk => {
    if (perk.type === 'once') {
      return xp >= perk.threshold;
    } else if (perk.type === 'capped') {
      const tier = calculatePerkTier(xp, perk.threshold, perk.value);
      return tier > 0;
    } else if (perk.type === 'infinite') {
      return xp >= perk.threshold;
    }
    return false;
  });
}

/**
 * Calculate total stats from all active disciplines
 */
export function calculateDisciplineStats(
  disciplines: DisciplineDefinition[],
  states: DisciplineState[]
): Record<string, number> {
  const stats: Record<string, number> = {};
  
  disciplines.forEach((discipline, index) => {
    const state = states[index];
    if (!state || state.paused) return;
    
    const bonus = calculateStatBonus(
      discipline.statBonus.baseValue,
      state.xp,
      discipline.scalingFactor
    );
    
    const statKey = discipline.statBonus.stat;
    stats[statKey] = (stats[statKey] || 0) + bonus;
  });
  
  return stats;
}