import type { SystemId } from '@/lib/rules';

/**
 * Encounter difficulty math for both systems. Pure and unit-tested.
 *  - 5e: DMG XP thresholds by character level + encounter multiplier by monster count.
 *  - PF2e: GMG XP budget by creature level relative to party level.
 */

export interface BudgetMonster {
  cr?: number | undefined; // 5e
  level?: number | undefined; // pf2e
}

export type Difficulty5e = 'trivial' | 'easy' | 'medium' | 'hard' | 'deadly';
export type DifficultyPf2e = 'trivial' | 'low' | 'moderate' | 'severe' | 'extreme';

export interface BudgetResult {
  system: SystemId;
  difficulty: Difficulty5e | DifficultyPf2e;
  /** number compared against the thresholds (5e: multiplier-adjusted XP) */
  ratingXp: number;
  /** raw XP awarded for defeating the monsters */
  totalXp: number;
  awardPerCharacter: number;
  thresholds: { label: string; value: number }[];
  monstersCounted: number;
}

// ---------------- 5e ----------------
const CR_XP: Record<number, number> = {
  0: 10, 0.125: 25, 0.25: 50, 0.5: 100, 1: 200, 2: 450, 3: 700, 4: 1100, 5: 1800,
  6: 2300, 7: 2900, 8: 3900, 9: 5000, 10: 5900, 11: 7200, 12: 8400, 13: 10000,
  14: 11500, 15: 13000, 16: 15000, 17: 18000, 18: 20000, 19: 22000, 20: 25000,
  21: 33000, 22: 41000, 23: 50000, 24: 62000, 25: 75000, 26: 90000, 27: 105000,
  28: 120000, 29: 135000, 30: 155000,
};

// [easy, medium, hard, deadly] per character level (DMG p.82)
const XP_THRESHOLDS: Record<number, [number, number, number, number]> = {
  1: [25, 50, 75, 100], 2: [50, 100, 150, 200], 3: [75, 150, 225, 400],
  4: [125, 250, 375, 500], 5: [250, 500, 750, 1100], 6: [300, 600, 900, 1400],
  7: [350, 750, 1100, 1700], 8: [450, 900, 1400, 2100], 9: [550, 1100, 1600, 2400],
  10: [600, 1200, 1900, 2800], 11: [800, 1600, 2400, 3600], 12: [1000, 2000, 3000, 4500],
  13: [1100, 2200, 3400, 5100], 14: [1250, 2500, 3800, 5700], 15: [1400, 2800, 4300, 6400],
  16: [1600, 3200, 4800, 7200], 17: [2000, 3900, 5900, 8800], 18: [2100, 4200, 6300, 9500],
  19: [2400, 4900, 7300, 10900], 20: [2800, 5700, 8500, 12700],
};

function crXp(cr: number): number {
  if (cr in CR_XP) return CR_XP[cr]!;
  // nearest known CR
  const keys = Object.keys(CR_XP).map(Number).sort((a, b) => a - b);
  let best = keys[0]!;
  for (const k of keys) if (Math.abs(k - cr) < Math.abs(best - cr)) best = k;
  return CR_XP[best]!;
}

function encounterMultiplier(count: number): number {
  if (count <= 1) return 1;
  if (count === 2) return 1.5;
  if (count <= 6) return 2;
  if (count <= 10) return 2.5;
  if (count <= 14) return 3;
  return 4;
}

function budget5e(partyLevels: number[], monsters: BudgetMonster[]): BudgetResult {
  const mons = monsters.filter((m) => m.cr !== undefined);
  const totalXp = mons.reduce((s, m) => s + crXp(m.cr!), 0);
  const ratingXp = Math.round(totalXp * encounterMultiplier(mons.length));

  const sum = [0, 0, 0, 0];
  for (const lvl of partyLevels) {
    const t = XP_THRESHOLDS[Math.max(1, Math.min(20, Math.round(lvl)))] ?? XP_THRESHOLDS[1]!;
    for (let i = 0; i < 4; i++) sum[i]! += t[i as 0 | 1 | 2 | 3];
  }
  const [easy, medium, hard, deadly] = sum;
  let difficulty: Difficulty5e = 'trivial';
  if (ratingXp >= deadly!) difficulty = 'deadly';
  else if (ratingXp >= hard!) difficulty = 'hard';
  else if (ratingXp >= medium!) difficulty = 'medium';
  else if (ratingXp >= easy!) difficulty = 'easy';

  return {
    system: '5e', difficulty, ratingXp, totalXp,
    awardPerCharacter: partyLevels.length ? Math.floor(totalXp / partyLevels.length) : totalXp,
    thresholds: [
      { label: 'Easy', value: easy! }, { label: 'Medium', value: medium! },
      { label: 'Hard', value: hard! }, { label: 'Deadly', value: deadly! },
    ],
    monstersCounted: mons.length,
  };
}

// ---------------- PF2e ----------------
const PF2E_CREATURE_XP: Record<number, number> = {
  [-4]: 10, [-3]: 15, [-2]: 20, [-1]: 30, 0: 40, 1: 60, 2: 80, 3: 120, 4: 160,
};
const PF2E_BASE = { trivial: 40, low: 60, moderate: 80, severe: 120, extreme: 160 };
const PF2E_ADJ = { trivial: 10, low: 15, moderate: 20, severe: 30, extreme: 40 };

function pf2eCreatureXp(diff: number): number {
  const clamped = Math.max(-4, Math.min(4, diff));
  return PF2E_CREATURE_XP[clamped] ?? 0;
}

function budgetPf2e(partyLevels: number[], monsters: BudgetMonster[]): BudgetResult {
  const partyLevel = partyLevels.length
    ? Math.round(partyLevels.reduce((a, b) => a + b, 0) / partyLevels.length)
    : 1;
  const partySize = partyLevels.length || 4;
  const mons = monsters.filter((m) => m.level !== undefined);
  const totalXp = mons.reduce((s, m) => s + pf2eCreatureXp(m.level! - partyLevel), 0);

  const adj = partySize - 4;
  const thr = {
    trivial: PF2E_BASE.trivial + adj * PF2E_ADJ.trivial,
    low: PF2E_BASE.low + adj * PF2E_ADJ.low,
    moderate: PF2E_BASE.moderate + adj * PF2E_ADJ.moderate,
    severe: PF2E_BASE.severe + adj * PF2E_ADJ.severe,
    extreme: PF2E_BASE.extreme + adj * PF2E_ADJ.extreme,
  };
  let difficulty: DifficultyPf2e = 'trivial';
  if (totalXp >= thr.extreme) difficulty = 'extreme';
  else if (totalXp >= thr.severe) difficulty = 'severe';
  else if (totalXp >= thr.moderate) difficulty = 'moderate';
  else if (totalXp >= thr.low) difficulty = 'low';

  return {
    system: 'pf2e', difficulty, ratingXp: totalXp, totalXp,
    awardPerCharacter: totalXp, // PF2e XP budget is already per-character
    thresholds: [
      { label: 'Trivial', value: thr.trivial }, { label: 'Low', value: thr.low },
      { label: 'Moderate', value: thr.moderate }, { label: 'Severe', value: thr.severe },
      { label: 'Extreme', value: thr.extreme },
    ],
    monstersCounted: mons.length,
  };
}

export function computeBudget(system: SystemId, partyLevels: number[], monsters: BudgetMonster[]): BudgetResult {
  return system === 'pf2e' ? budgetPf2e(partyLevels, monsters) : budget5e(partyLevels, monsters);
}

export const DIFFICULTY_COLOR: Record<string, string> = {
  trivial: 'text-muted', easy: 'text-success', low: 'text-success',
  medium: 'text-info', moderate: 'text-info', hard: 'text-warning',
  severe: 'text-warning', deadly: 'text-danger', extreme: 'text-danger',
};
