import fc from 'fast-check';
import { describe, it } from 'vitest';
import { Character } from './characters/Character.ts';
import { Level } from './value-objects/Level.ts';

describe('Healing', () => {
  describe('SelfHealIncreasesHealth', () => {
    it('property: healing increases health by the heal amount (when below cap)', () => {
      fc.assert(
        fc.property(
          fc.integer({ min: 0, max: 999 }),
          fc.integer({ min: 1, max: 500 }),
          (health, healAmount) => {
            // Constrain: ensure health + healAmount <= 1000 (level 1 cap)
            fc.pre(health + healAmount <= 1000);
            const c = Character.createWithHealth({
              name: 'hero',
              level: Level.create(1),
              health,
            });
            const result = c.healSelf(healAmount);
            return result.health.value === health + healAmount;
          },
        ),
      );
    });

    it('property: healing is capped at max health for level', () => {
      fc.assert(
        fc.property(
          fc.integer({ min: 900, max: 1000 }),
          fc.integer({ min: 1, max: 500 }),
          (health, healAmount) => {
            const c = Character.createWithHealth({
              name: 'hero',
              level: Level.create(1),
              health,
            });
            const result = c.healSelf(healAmount);
            const maxForLevel = Level.maxHealthForLevel(c.level.value);
            return result.health.value === Math.min(health + healAmount, maxForLevel);
          },
        ),
      );
    });

    it('property: healing at level 6+ is capped at 1500', () => {
      fc.assert(
        fc.property(
          fc.integer({ min: 1400, max: 1500 }),
          fc.integer({ min: 1, max: 500 }),
          (health, healAmount) => {
            // Constrain: ensure health + healAmount > 1500
            fc.pre(health + healAmount > 1500);
            const c = Character.createWithHealth({
              name: 'hero',
              level: Level.create(6),
              health,
            });
            const result = c.healSelf(healAmount);
            return result.health.value === 1500;
          },
        ),
      );
    });
  });

  describe('ZeroHeal', () => {
    it('property: zero heal changes nothing', () => {
      fc.assert(
        fc.property(fc.integer({ min: 0, max: 1000 }), (health) => {
          const c = Character.createWithHealth({
            name: 'hero',
            level: Level.create(1),
            health,
          });
          const result = c.healSelf(0);
          return result.health.value === health && result.status.kind === 'alive';
        }),
      );
    });
  });

  describe('DeadCannotHeal', () => {
    it('property: dead characters cannot heal — returns same reference, state unchanged', () => {
      fc.assert(
        fc.property(fc.integer({ min: 1, max: 500 }), (healAmount) => {
          const attacker = Character.create({ name: 'attacker', level: Level.create(1) });
          const hero = Character.create({ name: 'hero', level: Level.create(1) });
          // Kill the hero first using a different attacker
          const deadHero = attacker.dealDamage(hero, 10000);
          const healthBefore = deadHero.health.value;
          const statusBefore = deadHero.status.kind;
          // Try to heal the dead character
          const result = deadHero.healSelf(healAmount);
          return (
            result === deadHero &&
            result.health.value === healthBefore &&
            result.status.kind === statusBefore
          );
        }),
      );
    });
  });

  describe('NegativeHealForbidden', () => {
    it('property: negative heal amount throws an error', () => {
      fc.assert(
        fc.property(fc.integer({ min: -10000, max: -1 }), (negativeHeal) => {
          const c = Character.create({ name: 'hero', level: Level.create(1) });
          let threw = false;
          try {
            c.healSelf(negativeHeal);
          } catch {
            threw = true;
          }
          return threw;
        }),
      );
    });
  });
});
