import type { SystemId } from '@/lib/rules';
import type { Condition } from '@/lib/schemas/common';
import { conditionEffects } from './conditionEffects';
import type { AbilityKey, AdvState } from './types';

export interface MechanicalState {
  /** effective speed after conditions (0 if any condition zeroes it) */
  speed: number;
  /** advantage state on THIS creature's attack rolls (5e) */
  attackModifier: AdvState;
  /** advantage state on attacks made AGAINST this creature (5e) */
  attacksAgainstModifier: AdvState;
  /** cannot take actions/reactions */
  incapacitated: boolean;
  /** per-ability save penalties (auto-fail dominates disadvantage) */
  saveModifiers: Partial<Record<AbilityKey, 'disadvantage' | 'auto-fail'>>;
  /** disadvantage on ability checks (5e) */
  abilityCheckDisadvantage: boolean;
  /** pf2e: aggregated numeric status penalty (worst single value, penalties don't stack) */
  statusPenalty: number;
  /** short human-readable badges for the UI, e.g. "Speed 0", "Disadv. attacks" */
  badges: string[];
}

/** Combine two advantage states by the 5e rule: opposing sources cancel to normal. */
function combineAdv(a: AdvState, b: AdvState): AdvState {
  if (a === b) return a;
  if (a === 'normal') return b;
  if (b === 'normal') return a;
  return 'normal'; // one advantage + one disadvantage cancel
}

/**
 * Fold a creature's conditions into a single mechanical read-model. Pure and
 * synchronous — combat, the dice roller, and the player view all call this
 * (the player view ships the same table and derives effects locally from the
 * broadcast condition names, so no derived state crosses the wire).
 *
 * Unknown / homebrew condition names contribute nothing (they remain visible
 * labels), exactly as before.
 */
export function deriveState(system: SystemId, baseSpeed: number, conditions: Condition[]): MechanicalState {
  const table = conditionEffects(system);
  let speedZero = false;
  let attackModifier: AdvState = 'normal';
  let attacksAgainstModifier: AdvState = 'normal';
  let incapacitated = false;
  let abilityCheckDisadvantage = false;
  let statusPenalty = 0;
  const saveModifiers: Partial<Record<AbilityKey, 'disadvantage' | 'auto-fail'>> = {};

  for (const cond of conditions) {
    const eff = table[cond.name.trim().toLowerCase()];
    if (!eff) continue;
    if (eff.speedZero) speedZero = true;
    if (eff.incapacitated) incapacitated = true;
    if (eff.abilityChecks) abilityCheckDisadvantage = true;
    if (eff.attackRolls) attackModifier = combineAdv(attackModifier, eff.attackRolls);
    if (eff.attacksAgainst) attacksAgainstModifier = combineAdv(attacksAgainstModifier, eff.attacksAgainst);
    if (eff.saves) {
      for (const [ability, level] of Object.entries(eff.saves) as [AbilityKey, 'disadvantage' | 'auto-fail'][]) {
        // auto-fail dominates disadvantage
        if (level === 'auto-fail' || saveModifiers[ability] === undefined) saveModifiers[ability] = level;
      }
    }
    // pf2e: status penalties don't stack — take the worst single value.
    if (eff.statusPenalty && cond.value !== undefined) statusPenalty = Math.max(statusPenalty, cond.value);
  }

  const badges: string[] = [];
  if (speedZero) badges.push('Speed 0');
  if (incapacitated) badges.push('Incapacitated');
  if (attackModifier === 'disadvantage') badges.push('Disadv. attacks');
  if (attackModifier === 'advantage') badges.push('Adv. attacks');
  if (attacksAgainstModifier === 'advantage') badges.push('Attacked w/ adv.');
  if (statusPenalty > 0) badges.push(`−${statusPenalty} status`);

  return {
    speed: speedZero ? 0 : baseSpeed,
    attackModifier,
    attacksAgainstModifier,
    incapacitated,
    saveModifiers,
    abilityCheckDisadvantage,
    statusPenalty,
    badges,
  };
}
