import type { Character, Spellcasting } from '@/lib/schemas/character';
import type { EnforceResult } from './result';

/** Consume one spell slot at `level` (regular slots first, then matching pact slot). */
function consumeSlot(sc: Spellcasting, level: number): { spellcasting: Spellcasting; usedLevel: number } | null {
  const slot = sc.slots.find((s) => s.level === level);
  if (slot && slot.current > 0) {
    return {
      spellcasting: { ...sc, slots: sc.slots.map((s) => (s.level === level ? { ...s, current: s.current - 1 } : s)) },
      usedLevel: level,
    };
  }
  // Warlock pact magic — a single pooled slot at its own level.
  if (sc.pact && sc.pact.level === level && sc.pact.current > 0) {
    return {
      spellcasting: { ...sc, pact: { ...sc.pact, current: sc.pact.current - 1 } },
      usedLevel: level,
    };
  }
  return null;
}

/**
 * Cast a spell the character knows, at a chosen slot level (≥ the spell's own
 * level, to allow upcasting). Pure: decrements a slot, sets/replaces
 * concentration, and returns the patch + log. Cantrips cost no slot.
 *
 * Fails (no mutation) when no slot of the requested level is available — the UI
 * surfaces `reason` and lets the player pick a different level.
 */
export function castSpell(c: Character, spellId: string, atLevel?: number): EnforceResult {
  const spell = c.spellcasting.spells.find((s) => s.id === spellId);
  if (!spell) return { ok: false, reason: 'That spell is not on this character.' };

  const log: string[] = [];
  const patch: Partial<Character> = {};

  if (spell.level > 0) {
    const useLevel = Math.max(atLevel ?? spell.level, spell.level);
    const consumed = consumeSlot(c.spellcasting, useLevel);
    if (!consumed) return { ok: false, reason: `No level-${useLevel} slot available for ${spell.name}.` };
    patch.spellcasting = consumed.spellcasting;
    log.push(`Cast ${spell.name} using a level-${consumed.usedLevel} slot.`);
  } else {
    log.push(`Cast ${spell.name} (cantrip).`);
  }

  if (spell.concentration) {
    if (c.concentration && c.concentration.spellId !== spell.id) {
      log.push(`Concentration on ${c.concentration.spellName} ends.`);
    }
    patch.concentration = { spellId: spell.id, spellName: spell.name };
  }

  return { ok: true, patch, log };
}

/** Clear active concentration (e.g. the player drops it, or it breaks). */
export function dropConcentration(c: Character): Partial<Character> {
  return c.concentration ? { concentration: null } : {};
}

/** 5e Constitution save DC to maintain concentration after taking `damage`. */
export function concentrationDC(damage: number): number {
  return Math.max(10, Math.floor(damage / 2));
}
