import { AttackType } from '../monster/monster.entity';

// ---------- Types ----------

export interface CombatantStats {
  name: string;
  hpCurrent: number;
  hpMax: number;
  force: number;
  agilite: number;
  intelligence: number;
  chance: number;
  attack: number;       // arme (0 Sprint 2) pour joueur, flat pour monstre
  defense: number;      // 0 joueur Sprint 2
  attackType: AttackType;
}

export interface AttackResult {
  damage: number;
  isCrit: boolean;
  isDodged: boolean;
  log: string;
}

export interface RoundLog {
  round: number;
  playerAttack: AttackResult;
  monsterAttack: AttackResult;
  playerHp: number;
  monsterHp: number;
  log: string[];
}

export interface CombatResult {
  winner: 'player' | 'monster';
  rounds: RoundLog[];
  xpEarned: number;
  goldEarned: number;
  totalRounds: number;
}

// ---------- Formules (déterministes pour les tests, mais utilisent Math.random) ----------

const MAX_ROUNDS = 30; // sécurité anti-boucle infinie

function statForAttackType(stats: CombatantStats): number {
  switch (stats.attackType) {
    case 'melee':   return stats.force;
    case 'ranged':  return stats.agilite;
    case 'magic':   return stats.intelligence;
  }
}

export function calcPlayerDamage(player: CombatantStats, monsterDefense: number): number {
  const stat = statForAttackType(player);
  const raw = player.attack + Math.floor(stat * 1.5);
  return Math.max(1, raw - monsterDefense);
}

export function calcMonsterDamage(monster: CombatantStats, playerDefense: number): number {
  return Math.max(1, monster.attack - playerDefense);
}

export function rollCrit(chance: number): boolean {
  const rate = 0.05 + chance * 0.002;
  return Math.random() < rate;
}

export function rollDodge(chance: number): boolean {
  const rate = 0.05 + chance * 0.001;
  return Math.random() < rate;
}

function resolvePlayerAttack(
  player: CombatantStats,
  monster: CombatantStats,
): AttackResult {
  // Les monstres ne dodgent pas en Sprint 2
  const baseDamage = calcPlayerDamage(player, monster.defense);
  const isCrit = rollCrit(player.chance);
  const damage = isCrit ? Math.floor(baseDamage * 1.5) : baseDamage;

  const critText = isCrit ? ' (CRITIQUE !)' : '';
  const log = `${player.name} attaque ${monster.name} pour ${damage} dégâts${critText}`;

  return { damage, isCrit, isDodged: false, log };
}

function resolveMonsterAttack(
  monster: CombatantStats,
  player: CombatantStats,
): AttackResult {
  const isDodged = rollDodge(player.chance);
  if (isDodged) {
    return {
      damage: 0,
      isCrit: false,
      isDodged: true,
      log: `${player.name} esquive l'attaque de ${monster.name} !`,
    };
  }

  const damage = calcMonsterDamage(monster, player.defense);
  return {
    damage,
    isCrit: false,
    isDodged: false,
    log: `${monster.name} attaque ${player.name} pour ${damage} dégâts.`,
  };
}

// ---------- Résolution complète ----------

export function resolveCombat(
  player: CombatantStats,
  monster: CombatantStats,
  xpReward: number,
  goldMin: number,
  goldMax: number,
): CombatResult {
  let playerHp = player.hpCurrent;
  let monsterHp = monster.hpCurrent;
  const rounds: RoundLog[] = [];

  for (let round = 1; round <= MAX_ROUNDS; round++) {
    // -- Tour joueur --
    const playerAttack = resolvePlayerAttack(player, monster);
    monsterHp = Math.max(0, monsterHp - playerAttack.damage);

    // -- Tour monstre (seulement si encore vivant) --
    let monsterAttack: AttackResult;
    if (monsterHp > 0) {
      monsterAttack = resolveMonsterAttack(monster, player);
      playerHp = Math.max(0, playerHp - monsterAttack.damage);
    } else {
      monsterAttack = { damage: 0, isCrit: false, isDodged: false, log: '' };
    }

    rounds.push({
      round,
      playerAttack,
      monsterAttack,
      playerHp,
      monsterHp,
      log: [
        playerAttack.log,
        ...(monsterAttack.log ? [monsterAttack.log] : []),
        `HP — ${player.name}: ${playerHp}/${player.hpMax} | ${monster.name}: ${monsterHp}/${monster.hpCurrent}`,
      ],
    });

    if (monsterHp <= 0) {
      const goldEarned = goldMin + Math.floor(Math.random() * (goldMax - goldMin + 1));
      return { winner: 'player', rounds, xpEarned: xpReward, goldEarned, totalRounds: round };
    }

    if (playerHp <= 0) {
      return { winner: 'monster', rounds, xpEarned: 0, goldEarned: 0, totalRounds: round };
    }
  }

  // Timeout (ne devrait pas arriver avec des valeurs équilibrées)
  return { winner: 'monster', rounds, xpEarned: 0, goldEarned: 0, totalRounds: MAX_ROUNDS };
}

// ---------- Level up ----------

export function xpRequiredForLevel(level: number): number {
  return Math.round(100 * Math.pow(level, 1.5));
}

export interface LevelUpResult {
  newLevel: number;
  newXp: number;
  statPointsGained: number;
  levelsGained: number;
}

export function applyXpGain(currentLevel: number, currentXp: number, xpEarned: number): LevelUpResult {
  let level = currentLevel;
  let xp = currentXp + xpEarned;
  let statPointsGained = 0;

  // Chaîne de level up
  while (level < 100) {
    const required = xpRequiredForLevel(level + 1);
    if (xp >= required) {
      xp -= required;
      level++;
      statPointsGained += 5;
    } else {
      break;
    }
  }

  return {
    newLevel: level,
    newXp: xp,
    statPointsGained,
    levelsGained: level - currentLevel,
  };
}
