import { HEALER_ABILITY_IDS } from "../healers";
import type { AbilityLoadout, AbilitySlotId, HealerAbilityId } from "../types";
import { shuffleWithRandom } from "./random";
import type { RandomState } from "./types";
import { MAX_EQUIPPED_SPELLS, MAX_SPELL_RANK, SPELL_OFFERS_PER_WAVE, SPELL_PICKS_PER_WAVE } from "./types";

export interface RpgRoguelikeSpellPolicy {
  readonly blockedSpellIds: readonly HealerAbilityId[];
}

/**
 * RPG-owned content policy. Add an ability ID here to suppress it from future
 * drafts. Existing runs keep already-selected spells so saves stay valid.
 */
export const RPG_ROGUELIKE_SPELL_POLICY: RpgRoguelikeSpellPolicy = {
  blockedSpellIds: [
    // "paladin-crusader-strike",
  ],
};

export function buildRpgRoguelikeSpellPool(
  registeredSpellIds: readonly HealerAbilityId[],
  policy: RpgRoguelikeSpellPolicy = RPG_ROGUELIKE_SPELL_POLICY,
): HealerAbilityId[] {
  const blockedSpellIds = new Set(policy.blockedSpellIds);
  const eligibleSpellIds = registeredSpellIds.filter((spellId) => !blockedSpellIds.has(spellId));
  if (eligibleSpellIds.length < SPELL_OFFERS_PER_WAVE) {
    throw new Error(`RPG Roguelike needs at least ${SPELL_OFFERS_PER_WAVE} eligible healer abilities.`);
  }
  return eligibleSpellIds;
}

/** Compatibility export: canonical healer registry minus RPG-specific blocks. */
export const HEALER_SPELL_IDS: readonly HealerAbilityId[] = Object.freeze(
  buildRpgRoguelikeSpellPool(HEALER_ABILITY_IDS),
);

export const ABILITY_LOADOUT_SLOTS: readonly AbilitySlotId[] = [
  "ability1",
  "ability2",
  "ability3",
  "ability4",
  "ability5",
  "ability6",
];

export function generateSpellDraftOffers(
  source: RandomState,
  seenOfferIds: readonly HealerAbilityId[] = [],
  eligibleSpellIds: readonly HealerAbilityId[] = HEALER_SPELL_IDS,
): { offers: HealerAbilityId[]; random: RandomState } {
  const seen = new Set(seenOfferIds);
  const unseen = eligibleSpellIds.filter((spellId) => !seen.has(spellId));
  const pool = unseen.length >= SPELL_OFFERS_PER_WAVE ? unseen : eligibleSpellIds;
  const shuffled = shuffleWithRandom(source, pool);
  return { offers: shuffled.values.slice(0, SPELL_OFFERS_PER_WAVE), random: shuffled.random };
}

export function canPickSpell(
  selectedSpellIds: readonly HealerAbilityId[],
  picksThisWave: number,
  spellId: HealerAbilityId,
): boolean {
  return selectedSpellIds.length < MAX_EQUIPPED_SPELLS
    && picksThisWave < SPELL_PICKS_PER_WAVE
    && !selectedSpellIds.includes(spellId);
}

/** Final controller order is deterministic draft order after removals. */
export function abilityLoadoutFromSpellIds(spellIds: readonly HealerAbilityId[]): AbilityLoadout {
  const loadout: AbilityLoadout = {};
  for (let index = 0; index < Math.min(MAX_EQUIPPED_SPELLS, spellIds.length); index += 1) {
    loadout[ABILITY_LOADOUT_SLOTS[index]] = spellIds[index];
  }
  return loadout;
}

/** Fills the lowest open controller slot without moving retained spells. */
export function addSpellToAbilityLoadout(
  loadout: AbilityLoadout,
  spellId: HealerAbilityId,
): AbilityLoadout {
  if (Object.values(loadout).includes(spellId)) return loadout;
  const slotId = ABILITY_LOADOUT_SLOTS.find((candidate) => !loadout[candidate]);
  return slotId ? { ...loadout, [slotId]: spellId } : loadout;
}

/** Removes one spell while preserving every other controller binding. */
export function removeSpellFromAbilityLoadout(
  loadout: AbilityLoadout,
  spellId: HealerAbilityId,
): AbilityLoadout {
  const next: AbilityLoadout = {};
  for (const slotId of ABILITY_LOADOUT_SLOTS) {
    const current = loadout[slotId];
    if (current && current !== spellId) next[slotId] = current;
  }
  return next;
}

export function incrementSpellRank(
  ranks: Partial<Record<HealerAbilityId, number>>,
  spellId: HealerAbilityId,
): Partial<Record<HealerAbilityId, number>> {
  const current = Math.max(0, Math.min(MAX_SPELL_RANK, Math.floor(ranks[spellId] ?? 0)));
  return current >= MAX_SPELL_RANK ? { ...ranks } : { ...ranks, [spellId]: current + 1 };
}
