/**
 * Healing Object — a Magical Object that gives health to Characters.
 *
 * Inherits health/status management from MagicalObject.
 * Invariants enforced at construction:
 * - CurrentHealth never exceeds maxHealth
 */

import { Character } from '../characters/Character.ts';
import { Level } from '../value-objects/Level.ts';
import { MagicalObject } from './MagicalObject.ts';
import type { Healer } from './magical-object-types.ts';

export class HealingObject extends MagicalObject implements Healer {
  private constructor(
    health: number,
    maxHealth: number,
    status: { readonly kind: 'alive' } | { readonly kind: 'destroyed' },
  ) {
    super(health, maxHealth, status);
  }

  static create({
    maxHealth,
    currentHealth,
  }: {
    maxHealth: number;
    currentHealth: number;
  }): HealingObject {
    if (maxHealth < 0) throw new Error('MaxHealth cannot be negative');
    if (currentHealth < 0) throw new Error('CurrentHealth cannot be negative');
    if (currentHealth > maxHealth) throw new Error('CurrentHealth cannot exceed maxHealth');
    const status =
      currentHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
    return new HealingObject(currentHealth, maxHealth, status);
  }

  /** Use this object to heal a character. Returns updated object and character. */
  heal(character: Character, amount: number): { object: HealingObject; character: Character } {
    // Destroyed objects can't heal
    if (this.status.kind === 'destroyed') {
      return { object: this, character };
    }
    // Negative amount is invalid
    if (amount < 0) throw new Error('Heal amount must be non-negative');
    // Calculate actual heal amount: min of requested, object remaining, character headroom
    const objectRemaining = this.health;
    const characterMax = Level.maxHealthForLevel(character.level.value);
    const characterHeadroom = characterMax - character.health.value;
    const actualHeal = Math.min(amount, objectRemaining, characterHeadroom);
    // If actualHeal is 0, nothing changes
    if (actualHeal === 0) {
      return { object: this, character };
    }
    // Create updated object
    const newObjectHealth = this.health - actualHeal;
    const newObjectStatus =
      newObjectHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
    // Create updated character
    const newCharacterHealth = character.health.value + actualHeal;
    const newCharacter = Character.createWithHealth({
      name: character.name,
      level: character.level,
      health: newCharacterHealth,
    });
    return {
      object: new HealingObject(newObjectHealth, this.maxHealth, newObjectStatus),
      character: newCharacter,
    };
  }
}
