// ─── Invocation Utilities ──────────────────────────────────────────────────────
// Guardian/spell selection, charge rate, cost multiplier, drain multiplier

import { SPELLS_DEF } from '../constants/spells';
import { getGuardianForFloor } from '../data/guardian-encounters';
import { getMultiElementBonus, canAffordSpellCost, deductSpellCost } from './combat-utils';
import type { GuardianDef } from '../types';
import type { DisciplineBonuses } from './mana-utils';

// ─── Constants ─────────────────────────────────────────────────────────────────

const BASE_FILL_RATE = 0.25;
const BASE_DRAIN_RATE = 1.0;
const BASE_COST_MULTIPLIER = 0.1;
const MIN_COST_MULTIPLIER = 0.05;
const BASE_DRAIN_MULTIPLIER = 1.0;
const MIN_DRAIN_MULTIPLIER = 0.7;
const MAX_CAST_SPEED_BONUS = 0.5;
const PACT_AFFINITY_SCALING = 1.5;

// ─── Invocation Guardian Selection ─────────────────────────────────────────────

export interface ActiveInvocation {
  guardianFloor: number;
  spellId: string;
  element: string;
  castProgress: number;
}

/**
 * Select the best guardian to channel based on current enemy.
 * Scores all signed pacts by: bestElementalBonus × tierMultiplier
 * Returns the floor number of the best guardian, or null if no signed pacts.
 */
export function selectInvocationGuardian(
  signedPacts: number[],
  enemyElements: string[],
): number | null {
  if (signedPacts.length === 0) return null;

  let bestFloor: number | null = null;
  let bestScore = -Infinity;

  for (const floor of signedPacts) {
    const guardian = getGuardianForFloor(floor);
    if (!guardian) continue;

    // Best elemental bonus across all guardian elements
    const elementalBonus = getMultiElementBonus(
      guardian.element[0],
      enemyElements,
    );
    // Use the best bonus among all guardian elements
    let bestElemBonus = elementalBonus;
    for (const elem of guardian.element) {
      const bonus = getMultiElementBonus(elem, enemyElements);
      if (bonus > bestElemBonus) bestElemBonus = bonus;
    }

    const tierMultiplier = 1.0 + floor * 0.005;
    const score = bestElemBonus * tierMultiplier;

    if (score > bestScore) {
      bestScore = score;
      bestFloor = floor;
    }
  }

  return bestFloor;
}

// ─── Guardian Spellbook ────────────────────────────────────────────────────────

/**
 * Get all spells a guardian knows (union of all their elements' spells).
 */
export function getGuardianSpellbook(
  guardian: GuardianDef,
): typeof SPELLS_DEF[string][] {
  const spellIds = new Set<string>();
  const spells: typeof SPELLS_DEF[string][] = [];

  for (const element of guardian.element) {
    for (const spell of Object.values(SPELLS_DEF)) {
      if (spell.elem === element && !spellIds.has(spell.name)) {
        spellIds.add(spell.name);
        spells.push(spell);
      }
    }
  }

  return spells;
}

// ─── Spell Selection ───────────────────────────────────────────────────────────

/**
 * Select the best affordable spell from a spellbook.
 * Picks highest-damage spell the player can afford at the effective cost multiplier.
 * Returns { spellId, element } or null if nothing is affordable.
 */
export function selectInvocationSpell(
  spellbook: typeof SPELLS_DEF[string][],
  rawMana: number,
  elements: Record<string, { current: number; max: number; unlocked: boolean }>,
  costMultiplier: number,
): { spellId: string; element: string } | null {
  // Filter to affordable spells at the effective cost multiplier
  const affordable = spellbook.filter((spell) => {
    const scaledCost = {
      type: spell.cost.type as 'raw' | 'element',
      element: spell.cost.element,
      amount: spell.cost.amount * costMultiplier,
    };
    return canAffordSpellCost(scaledCost, rawMana, elements);
  });

  if (affordable.length === 0) return null;

  // Sort by highest damage, then highest tier, then lowest cost (most efficient)
  affordable.sort((a, b) => {
    if (b.dmg !== a.dmg) return b.dmg - a.dmg;
    if (b.tier !== a.tier) return b.tier - a.tier;
    return a.cost.amount - b.cost.amount;
  });

  const best = affordable[0];
  return { spellId: best.name, element: best.elem };
}

// ─── Deduct Invocation Spell Cost ─────────────────────────────────────────────

/**
 * Deduct the cost of an invocation spell at the effective cost multiplier.
 */
export function deductInvocationSpellCost(
  spellId: string,
  costMultiplier: number,
  rawMana: number,
  elements: Record<string, { current: number; max: number; unlocked: boolean }>,
): { rawMana: number; elements: Record<string, { current: number; max: number; unlocked: boolean }> } {
  const spell = SPELLS_DEF[spellId];
  if (!spell) return { rawMana, elements };

  const scaledCost = {
    type: spell.cost.type as 'raw' | 'element',
    element: spell.cost.element,
    amount: spell.cost.amount * costMultiplier,
  };
  return deductSpellCost(scaledCost, rawMana, elements);
}

// ─── Charge Fill Rate ──────────────────────────────────────────────────────────

/**
 * Compute charge fill rate per tick.
 * chargePerTick = baseFillRate × (1 + pacts × 0.15) × (1 + chargeRateBonus)
 */
export function computeChargeFillRate(
  signedPactsLength: number,
  chargeRateBonus: number,
): number {
  const pactCountMultiplier = 1 + signedPactsLength * 0.15;
  const disciplineMultiplier = 1 + chargeRateBonus;
  return BASE_FILL_RATE * pactCountMultiplier * disciplineMultiplier;
}

// ─── Cast Speed Bonus ──────────────────────────────────────────────────────────

/**
 * Compute cast speed bonus from pact affinity.
 * castSpeedBonus = MAX_BONUS × (1 - 1 / (1 + pactAffinity × SCALING))
 */
export function computeCastSpeedBonus(pactAffinity: number): number {
  return MAX_CAST_SPEED_BONUS * (1 - 1 / (1 + pactAffinity * PACT_AFFINITY_SCALING));
}

// ─── Cost Multiplier ───────────────────────────────────────────────────────────

/**
 * Compute effective cost multiplier from discipline bonuses.
 * Base is 0.1, reduced by invocationCostReduction. Minimum: 0.05
 */
export function computeCostMultiplier(disciplineBonuses: DisciplineBonuses): number {
  const reduction = disciplineBonuses.bonuses.invocationCostReduction || 0;
  return Math.max(MIN_COST_MULTIPLIER, BASE_COST_MULTIPLIER - reduction);
}

// ─── Drain Rate Multiplier ─────────────────────────────────────────────────────

/**
 * Compute drain rate multiplier from discipline bonuses.
 * Base is 1.0, reduced by drainRateMultiplier (which is negative).
 * Minimum: 0.7
 */
export function computeDrainRateMultiplier(disciplineBonuses: DisciplineBonuses): number {
  const reduction = disciplineBonuses.bonuses.drainRateMultiplier || 0;
  return Math.max(MIN_DRAIN_MULTIPLIER, BASE_DRAIN_MULTIPLIER + reduction);
}

// ─── Drain Per Tick ────────────────────────────────────────────────────────────

/**
 * Compute charge drain per tick during invocation.
 * drainPerTick = BASE_DRAIN × (spellCost / 10) × drainRateMultiplier
 */
export function computeDrainPerTick(
  spellCostAmount: number,
  drainRateMultiplier: number,
): number {
  const spellCostMultiplier = spellCostAmount / 10;
  return BASE_DRAIN_RATE * spellCostMultiplier * drainRateMultiplier;
}
