// ─── Unified Conversion Rate Calculator ───────────────────────────────────────
// Computes conversion rates for all elements using the unified formula:
//
//   finalRate = (disciplineRate + attunementBase + pactBase)
//               × (1 + attunementLevelBonus + pactLevelBonus)
//               × meditationMult
//
// All costs are deducted from regen, not from the mana pool.

import { CONVERSION_COSTS, getConversionCost } from '../data/conversion-costs';
import { getElementDistance } from './element-distance';
import type { DisciplineEffectsResult } from '../effects/discipline-effects';

// ─── Types ────────────────────────────────────────────────────────────────────

export interface ConversionRateEntry {
  element: string;
  distance: number;
  /** Base rate from disciplines (includes XP scaling + perks) */
  disciplineRate: number;
  /** Base rate from attunements */
  attunementBase: number;
  /** Base rate from guardian pacts */
  pactBase: number;
  /** Sum of base rates */
  baseRate: number;
  /** Attunement level multiplier: 1 + Σ(1.5^(attunementLevel-1) - 1) per relevant attunement */
  attunementMult: number;
  /** Pact level multiplier: 1 + Σ(pactCount_element × invokerLevel × 0.25) */
  pactMult: number;
  /** Meditation multiplier (reduced by distance) */
  meditationMult: number;
  /** Final effective rate (per hour) */
  finalRate: number;
  /** Raw cost per unit of destination */
  rawCost: number;
  /** Component costs per unit of destination */
  componentCosts: Record<string, number>;
  /** Whether this conversion is paused due to insufficient regen */
  paused: boolean;
  /** Reason for pausing (which source is insufficient) */
  pauseReason: string | null;
}

export interface ConversionRateResult {
  /** Per-element conversion rates */
  rates: Record<string, ConversionRateEntry>;
  /** Total raw regen drain per hour */
  totalRawDrain: number;
  /** Per-element regen drain per hour (as component) */
  elementDrain: Record<string, number>;
}

// ─── Attunement Base Rates (per spec §5) ──────────────────────────────────────

const ATTUNEMENT_BASE_RATES: Record<string, number> = {
  transference: 0.2, // Enchanter
  earth: 0.25,       // Fabricator
};

// ─── Main Calculator ──────────────────────────────────────────────────────────

export interface ConversionRateParams {
  /** Discipline effects (includes conversion stat bonuses) */
  disciplineEffects: DisciplineEffectsResult;
  /** Active attunements: id → { level } */
  attunements: Record<string, { active: boolean; level: number }>;
  /** Signed pact floor numbers */
  signedPacts: number[];
  /** Guardian element lookup: floor → primary element */
  pactElementMap: Record<number, string>;
  /** Invoker attunement level (for pact level bonus) */
  invokerLevel: number;
  /** Current meditation multiplier (1 = not meditating) */
  meditationMultiplier: number;
  /** Current gross regen per element (before conversion drains) */
  grossRegen: Record<string, number>;
  /** Raw gross regen */
  rawGrossRegen: number;
}

/**
 * Compute unified conversion rates for all elements.
 * Returns per-element rates and regen drain totals.
 */
export function computeConversionRates(params: ConversionRateParams): ConversionRateResult {
  const {
    disciplineEffects,
    attunements,
    signedPacts,
    pactElementMap,
    invokerLevel,
    meditationMultiplier,
    grossRegen,
    rawGrossRegen,
  } = params;

  const rates: Record<string, ConversionRateEntry> = {};
  const elementDrain: Record<string, number> = {};
  let totalRawDrain = 0;

  // ── Step 1: Compute attunement level bonuses per element ──────────
  // Each attunement contributes 1.5^(level-1) exponential scaling to conversions
  // where the attunement's primary element is the destination or a component.
  const attunementBonuses: Record<string, number> = {};
  for (const [id, state] of Object.entries(attunements)) {
    if (!state.active) continue;
    const level = state.level || 1;
    const bonus = Math.pow(1.5, level - 1) - 1;

    // Determine which elements this attunement boosts based on its primary mana type
    for (const [elem, cost] of Object.entries(CONVERSION_COSTS)) {
      const isDestination = elem === getAttunementPrimaryElement(id);
      const isComponent = Object.keys(cost.componentCosts).includes(getAttunementPrimaryElement(id));
      if (isDestination || isComponent) {
        attunementBonuses[elem] = (attunementBonuses[elem] || 0) + bonus;
      }
    }
  }

  // ── Step 2: Compute pact bonuses per element ──────────────────────
  const pactBaseRates: Record<string, number> = {};
  const pactBonuses: Record<string, number> = {};
  for (const floor of signedPacts) {
    const element = pactElementMap[floor];
    if (!element) continue;
    pactBaseRates[element] = (pactBaseRates[element] || 0) + 0.15;
    pactBonuses[element] = (pactBonuses[element] || 0) + invokerLevel * 0.25;
  }

  // ── Step 3: Compute rates for each element ────────────────────────
  for (const [elem, cost] of Object.entries(CONVERSION_COSTS)) {
    const distance = cost.distance;

    // Discipline rate: from disciplineEffects.conversions or stat bonuses
    const discRate = disciplineEffects.conversions[elem]?.rate
      || disciplineEffects.bonuses[`conversion_${elem}`]
      || 0;

    // Attunement base rate
    const attBase = ATTUNEMENT_BASE_RATES[elem] || 0;

    // Pact base rate
    const pactBase = pactBaseRates[elem] || 0;

    // Combined base rate
    const baseRate = discRate + attBase + pactBase;

    // Multipliers
    const attMult = 1 + (attunementBonuses[elem] || 0);
    const pactMult = 1 + (pactBonuses[elem] || 0);

    // Meditation multiplier (reduced by distance)
    const medMult = distance > 0
      ? 1 + (meditationMultiplier - 1) / distance
      : 1;

    // Final rate
    const finalRate = baseRate * attMult * pactMult * medMult;

    // Check if paused (insufficient regen for any source)
    let paused = false;
    let pauseReason: string | null = null;
    const rawDrain = finalRate * cost.rawCost;

    if (rawDrain > rawGrossRegen) {
      paused = true;
      pauseReason = `Insufficient raw regen (need ${rawDrain.toFixed(2)}/hr, have ${rawGrossRegen.toFixed(2)}/hr)`;
    } else {
      for (const [comp, compCost] of Object.entries(cost.componentCosts)) {
        const compDrain = finalRate * compCost;
        const compGross = grossRegen[comp] || 0;
        if (compDrain > compGross) {
          paused = true;
          pauseReason = `Insufficient ${comp} regen (need ${compDrain.toFixed(2)}/hr, have ${compGross.toFixed(2)}/hr)`;
          break;
        }
      }
    }

    // Only accumulate drains for active (non-paused) conversions
    if (!paused) {
      totalRawDrain += rawDrain;
      for (const [comp, compCost] of Object.entries(cost.componentCosts)) {
        elementDrain[comp] = (elementDrain[comp] || 0) + finalRate * compCost;
      }
    }

    rates[elem] = {
      element: elem,
      distance,
      disciplineRate: discRate,
      attunementBase: attBase,
      pactBase,
      baseRate,
      attunementMult: attMult,
      pactMult,
      meditationMult: medMult,
      finalRate: paused ? 0 : finalRate,
      rawCost: cost.rawCost,
      componentCosts: { ...cost.componentCosts },
      paused,
      pauseReason,
    };
  }

  return { rates, totalRawDrain, elementDrain };
}

// ─── Helpers ──────────────────────────────────────────────────────────────────

function getAttunementPrimaryElement(attunementId: string): string {
  const map: Record<string, string> = {
    enchanter: 'transference',
    fabricator: 'earth',
  };
  return map[attunementId] || '';
}

/**
 * Compute the meditation multiplier for a specific element's conversion.
 * Full strength for distance-1, half for distance-2, etc.
 */
export function getMeditationConversionMult(
  meditationMultiplier: number,
  elementDistance: number,
): number {
  if (elementDistance <= 0) return 1;
  return 1 + (meditationMultiplier - 1) / elementDistance;
}
