/**
 * Centralized Stat Utilities
 *
 * All stat modifications should use these functions to ensure proper clamping.
 * Stats are bounded 0-100 unless otherwise specified.
 */

import { Person } from '../models/Person.js';
import { Player } from '../models/Player.js';

/**
 * Deduct energy from a person, clamping to [0, 100].
 * Use this instead of `person.energy -= X` to prevent negative energy.
 */
export function deductEnergy(person: { energy?: number }, amount: number): void {
  person.energy = Math.max(0, (person.energy ?? 100) - Math.abs(amount));
}

// Stat bounds configuration
export const STAT_BOUNDS = {
  energy: { min: 0, max: 100, default: 100 },
  happiness: { min: 0, max: 100, default: 50 },
  health: { min: 0, max: 100, default: 100 },
  social: { min: 0, max: 100, default: 50 },
  stress: { min: 0, max: 100, default: 0 },
  hunger: { min: 0, max: 100, default: 0 },
  thirst: { min: 0, max: 100, default: 0 },
  affinity: { min: -100, max: 100, default: 50 },
  money: { min: 0, max: Infinity, default: 0 },
} as const;

type StatName = keyof typeof STAT_BOUNDS;

/**
 * Clamp a value to the specified bounds
 */
export function clamp(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, value));
}

/**
 * Safely modify a stat, clamping to valid range
 */
export function modifyStat(
  currentValue: number | undefined,
  delta: number,
  statName: StatName = 'energy'
): number {
  const bounds = STAT_BOUNDS[statName];
  const current = currentValue ?? bounds.default;
  return clamp(current + delta, bounds.min, bounds.max);
}

/**
 * Safely set a stat, clamping to valid range
 */
export function setStat(value: number, statName: StatName = 'energy'): number {
  const bounds = STAT_BOUNDS[statName];
  return clamp(value, bounds.min, bounds.max);
}

/**
 * Clamp all stats on a character to valid ranges
 * Use this to fix corrupted data
 */
export function clampAllStats(person: Person): void {
  if (person.energy !== undefined) {
    person.energy = clamp(person.energy, STAT_BOUNDS.energy.min, STAT_BOUNDS.energy.max);
  }
  if (person.happiness !== undefined) {
    person.happiness = clamp(person.happiness, STAT_BOUNDS.happiness.min, STAT_BOUNDS.happiness.max);
  }
  if (person.health !== undefined) {
    person.health = clamp(person.health, STAT_BOUNDS.health.min, STAT_BOUNDS.health.max);
  }
  if (person.social !== undefined) {
    person.social = clamp(person.social, STAT_BOUNDS.social.min, STAT_BOUNDS.social.max);
  }
  if (person.stress !== undefined) {
    person.stress = clamp(person.stress, STAT_BOUNDS.stress.min, STAT_BOUNDS.stress.max);
  }
  if (person.hunger !== undefined) {
    person.hunger = clamp(person.hunger, STAT_BOUNDS.hunger.min, STAT_BOUNDS.hunger.max);
  }
  if (person.thirst !== undefined) {
    person.thirst = clamp(person.thirst, STAT_BOUNDS.thirst.min, STAT_BOUNDS.thirst.max);
  }
  if (person.affinity !== undefined) {
    person.affinity = clamp(person.affinity, STAT_BOUNDS.affinity.min, STAT_BOUNDS.affinity.max);
  }
}

/**
 * Clamp all stats on the player character and all relationships
 */
export function clampPlayerStats(player: Player): void {
  // Clamp main character stats
  clampAllStats(player.c);

  // Clamp all relationship character stats
  if (player.r) {
    for (const person of player.r) {
      clampAllStats(person);
    }
  }
}

/**
 * Enforce romantic relationship exclusivity
 * Removes all romantic relationships except the most recent one
 */
export function enforceRelationshipExclusivity(player: Player): { removed: string[], kept: string | null } {
  const romanticTypes = ['partner', 'girlfriend', 'boyfriend', 'dating', 'spouse', 'wife', 'husband', 'fiancé', 'fiancée', 'engaged'];
  const removed: string[] = [];
  let kept: string | null = null;

  if (!player.r) return { removed, kept };

  // Find all characters with romantic relationships
  const romanticPartners = player.r.filter(p =>
    p.relationships?.some(r => romanticTypes.includes(r.toLowerCase()))
  );

  if (romanticPartners.length <= 1) {
    // No conflict
    kept = romanticPartners[0]?.firstname ?? null;
    return { removed, kept };
  }

  // Sort by familiarity (higher = more established relationship)
  romanticPartners.sort((a, b) => (b.familiarity ?? 0) - (a.familiarity ?? 0));

  // Keep the first one (highest familiarity), downgrade others
  const toKeep = romanticPartners[0];
  kept = toKeep.firstname;

  for (let i = 1; i < romanticPartners.length; i++) {
    const partner = romanticPartners[i];
    removed.push(partner.firstname);

    // Remove romantic relationship types
    if (partner.relationships) {
      partner.relationships = partner.relationships.filter(
        r => !romanticTypes.includes(r.toLowerCase())
      );
      // Add 'ex' if they were dating
      if (!partner.relationships.includes('ex')) {
        partner.relationships.push('ex');
      }
    }
  }

  return { removed, kept };
}

/**
 * Clean up stale pending events
 */
export function cleanStalePendingEvents(player: Player): number {
  if (!player.pendingConversationEvents) return 0;

  const now = new Date();
  const originalCount = player.pendingConversationEvents.length;

  // Remove expired events and events for characters that no longer exist or are ex
  player.pendingConversationEvents = player.pendingConversationEvents.filter(event => {
    // Check if expired
    if (event.expiresAt && new Date(event.expiresAt) < now) {
      return false;
    }

    // Check if character exists
    const character = player.r?.find(p => p.id === event.characterId);
    if (!character) {
      return false;
    }

    // Remove relationship events for ex partners
    if (character.relationships?.includes('ex')) {
      const relationshipEventTypes = ['relationship_accepted', 'confession', 'date_request', 'emotional_moment'];
      if (relationshipEventTypes.includes(event.type)) {
        return false;
      }
    }

    return true;
  });

  return originalCount - player.pendingConversationEvents.length;
}
