/**
 * Habit Manager Module
 * Dedicated module for habit tracking and management
 *
 * This module provides a cleaner API for the habit system, re-exporting
 * habit-related functionality from health_manager.ts and stats_manager.ts.
 *
 * Habit System Overview:
 * ----------------------
 * - Characters have both positive and negative habits
 * - Negative habits can be quit through a 30-week process
 * - Quitting habits costs +5 peak energy per quitting habit
 * - Habits are assigned based on age (fewer habits for younger characters)
 * - Conflicting positive/negative habits cannot coexist (e.g., tardiness vs punctuality)
 *
 * Ported from Python:
 * - ws/health/health_manager.py (HabitClass, setHabits, quitHabit, stopQuitHabit, handleHabitChanges)
 * - ws/stats/stats_manager.py (getPeakEnergy - habit energy cost calculation)
 */

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

// ============================================================
// Types and Interfaces
// ============================================================

/**
 * Type of habit - positive habits help the character, negative habits hinder
 */
export type HabitType = 'positive' | 'negative';

/**
 * Current status of a habit
 * - 'active': Habit is currently affecting the character
 * - 'quitting': Character is in the process of quitting (30-week countdown)
 */
export type HabitStatus = 'active' | 'quitting';

/**
 * Habit interface representing a character's behavioral pattern
 */
export interface Habit {
  /** Internal name identifier (e.g., 'smoking', 'regular_exercise') */
  name: string;
  /** User-facing description of the habit */
  description: string;
  /** Type discriminator, always 'habit' */
  type: 'habit';
  /** Whether this is a positive or negative habit */
  habitType: HabitType;
  /** Current status of the habit */
  status: HabitStatus;
  /** Progress toward quitting (0-30 weeks) */
  quitProgress: number;
}

/**
 * Result of a habit operation (quit, stop quit, etc.)
 */
export interface HabitOperationResult {
  success: boolean;
  message: string;
  habit?: Habit;
}

// ============================================================
// Habit Definitions
// ============================================================

/**
 * Create a new habit instance
 *
 * @param name - Internal identifier for the habit
 * @param description - User-facing description
 * @param habitType - 'positive' or 'negative' (defaults to 'negative')
 * @returns A new Habit object
 */
export function createHabit(
  name: string,
  description: string,
  habitType: HabitType = 'negative'
): Habit {
  return {
    name,
    description,
    type: 'habit',
    habitType,
    status: 'active',
    quitProgress: 0,
  };
}

/**
 * All negative habits that can afflict a character
 * These habits have negative effects on the character's life
 */
export const negativeHabits: Record<string, Habit> = {
  tardiness: createHabit('tardiness', 'Your lack of punctuality is becoming noticeable.'),
  nail_biting: createHabit('nail_biting', "You catch yourself biting your nails. It's a nervous habit."),
  overthinking: createHabit('overthinking', "You can't help but overanalyze every situation."),
  procrastination: createHabit('procrastination', 'You tend to put things off until the last minute.'),
  negative_thinking: createHabit('negative_thinking', "You're struggling to keep negative thoughts at bay."),
  overeating: createHabit(
    'overeating',
    "Your tiredness is making you reach for comfort foods. You're overeating and it's not making you feel any better."
  ),
  under_exercising: createHabit('under_exercising', "You haven't been getting much exercise lately."),
  excessive_screen_time: createHabit('excessive_screen_time', "You're spending too much time in front of screens."),
  impulsiveness: createHabit('impulsiveness', 'You have a tendency to act without thinking things through.'),
  indecisiveness: createHabit('indecisiveness', 'You struggle with making decisions, big or small.'),
  neglecting_self_care: createHabit(
    'neglecting_self_care',
    "You haven't been taking care of yourself as much as you should."
  ),
  poor_time_management: createHabit('poor_time_management', "You're always running behind schedule."),
  overeating_when_stressed: createHabit('overeating_when_stressed', 'Stress pushes you to eat more than you need.'),
  excessive_caffeine_intake: createHabit('excessive_caffeine_intake', "You've been consuming too much caffeine."),
  smoking: createHabit('smoking', 'The habit of smoking is taking a toll on your health.'),
  excessive_alcohol_consumption: createHabit(
    'excessive_alcohol_consumption',
    'Exhaustion sets in and you find yourself reaching for a drink more often than usual.'
  ),
};

/**
 * All positive habits a character can develop
 * These habits have beneficial effects on the character's life
 */
export const positiveHabits: Record<string, Habit> = {
  punctuality: createHabit('punctuality', 'Your punctuality is appreciated by those around you.', 'positive'),
  regular_exercise: createHabit('regular_exercise', 'Your commitment to regular exercise is paying off.', 'positive'),
  healthy_eating: createHabit('healthy_eating', 'You feel better when you eat well.', 'positive'),
  positive_thinking: createHabit('positive_thinking', 'Your positive mindset helps you overcome challenges.', 'positive'),
  planning_ahead: createHabit('planning_ahead', 'Planning ahead makes life smoother.', 'positive'),
  prioritizing_self_care: createHabit('prioritizing_self_care', 'Taking care of yourself improves your mood.', 'positive'),
  effective_time_management: createHabit(
    'effective_time_management',
    'Your time management skills keep things under control.',
    'positive'
  ),
  practicing_gratitude: createHabit('practicing_gratitude', 'Practicing gratitude makes you feel happier.', 'positive'),
  mindfulness_meditation: createHabit('mindfulness_meditation', 'Meditation helps keep your mind clear.', 'positive'),
  regular_sleeping_schedule: createHabit(
    'regular_sleeping_schedule',
    'Maintaining a regular sleep schedule keeps you refreshed.',
    'positive'
  ),
  active_listening: createHabit('active_listening', 'You understand others better by actively listening.', 'positive'),
  showing_empathy: createHabit('showing_empathy', 'Showing empathy builds strong relationships.', 'positive'),
  tidy: createHabit('tidy', 'Keeping things tidy helps your peace of mind.', 'positive'),
  hygienic: createHabit('hygienic', 'Your hygiene habits keep you healthy.', 'positive'),
  work_life_balance: createHabit('work_life_balance', 'Balancing work and personal time keeps you satisfied.', 'positive'),
};

/**
 * Mapping of negative habits to their positive counterparts
 * Characters cannot have both a negative habit and its positive pair simultaneously
 */
export const habitPairs: Record<string, string> = {
  tardiness: 'punctuality',
  overthinking: 'positive_thinking',
  procrastination: 'planning_ahead',
  negative_thinking: 'positive_thinking',
  overeating: 'healthy_eating',
  under_exercising: 'regular_exercise',
  excessive_screen_time: 'work_life_balance',
  impulsiveness: 'planning_ahead',
  indecisiveness: 'planning_ahead',
  neglecting_self_care: 'prioritizing_self_care',
  poor_time_management: 'effective_time_management',
  overeating_when_stressed: 'healthy_eating',
  excessive_caffeine_intake: 'regular_sleeping_schedule',
  smoking: 'prioritizing_self_care',
  excessive_alcohol_consumption: 'prioritizing_self_care',
};

// ============================================================
// Helper Functions
// ============================================================

/**
 * Get a random sample from an array
 * Fisher-Yates shuffle approach for unbiased sampling
 *
 * @param arr - Source array to sample from
 * @param count - Number of items to sample
 * @returns Array of sampled items
 */
function randomSample<T>(arr: T[], count: number): T[] {
  const shuffled = [...arr].sort(() => Math.random() - 0.5);
  return shuffled.slice(0, Math.min(count, arr.length));
}

/**
 * Format a habit name for display (convert snake_case to Title Case)
 *
 * @param name - The habit name in snake_case
 * @returns Formatted display name
 */
export function formatHabitName(name: string): string {
  return name.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
}

/**
 * Get all habits for a person
 *
 * @param person - The person to get habits for
 * @returns Array of habits (empty if none)
 */
export function getHabits(person: Person): Habit[] {
  return (person as unknown as { habits?: Habit[] }).habits ?? [];
}

/**
 * Get only negative habits for a person
 *
 * @param person - The person to get habits for
 * @returns Array of negative habits
 */
export function getNegativeHabits(person: Person): Habit[] {
  return getHabits(person).filter((h) => h.habitType === 'negative');
}

/**
 * Get only positive habits for a person
 *
 * @param person - The person to get habits for
 * @returns Array of positive habits
 */
export function getPositiveHabits(person: Person): Habit[] {
  return getHabits(person).filter((h) => h.habitType === 'positive');
}

/**
 * Get habits that are currently being quit
 *
 * @param person - The person to check
 * @returns Array of habits in 'quitting' status
 */
export function getQuittingHabits(person: Person): Habit[] {
  return getHabits(person).filter((h) => h.status === 'quitting');
}

/**
 * Check if a person has a specific habit
 *
 * @param person - The person to check
 * @param habitName - Name of the habit to look for
 * @returns true if the person has the habit
 */
export function hasHabit(person: Person, habitName: string): boolean {
  return getHabits(person).some((h) => h.name === habitName);
}

/**
 * Find a specific habit on a person
 *
 * @param person - The person to check
 * @param habitName - Name of the habit to find
 * @returns The habit if found, undefined otherwise
 */
export function findHabit(person: Person, habitName: string): Habit | undefined {
  return getHabits(person).find((h) => h.name === habitName);
}

// ============================================================
// Core Habit Functions
// ============================================================

/**
 * Initialize habits for a person based on their age
 *
 * Habit assignment rules:
 * - Base habit count is 6 for adults (18+)
 * - Children get fewer habits: floor(age / 5)
 * - Random negative habits are assigned first
 * - Remaining slots filled with non-conflicting positive habits
 *
 * @param person - Person object to assign habits to
 * @returns The updated person object
 */
export function setHabits(person: Person): Person {
  const personAny = person as unknown as { habits?: Habit[] };

  // Determine max habits based on age
  let habitCount = 6;
  if (person.ageYears < 18) {
    habitCount = Math.round(person.ageYears / 5);
  }

  // Get random negative habits (0 to habitCount)
  const negativeHabitValues = Object.values(negativeHabits).map((h) => ({ ...h }));
  const personNegativeHabits = randomSample(negativeHabitValues, Math.floor(Math.random() * (habitCount + 1)));

  // Remaining slots for positive habits
  habitCount = habitCount - personNegativeHabits.length;

  // Find conflicting positive habits that cannot be assigned
  const negativeHabitNames = personNegativeHabits.map((h) => h.name);
  const conflictingPositive = negativeHabitNames.map((name) => habitPairs[name]).filter(Boolean);

  // Filter out conflicting positive habits
  const nonConflictingPositive = Object.values(positiveHabits)
    .map((h) => ({ ...h }))
    .filter((h) => !conflictingPositive.includes(h.name));

  // Get random non-conflicting positive habits
  const personPositiveHabits = randomSample(
    nonConflictingPositive,
    Math.min(Math.floor(Math.random() * (habitCount + 1)), nonConflictingPositive.length)
  );

  // Assign habits to person
  personAny.habits = [...personNegativeHabits, ...personPositiveHabits];

  return person;
}

/**
 * Start the process of quitting a habit
 *
 * When a character decides to quit a habit:
 * 1. The habit's status is set to 'quitting'
 * 2. A message is added to the player's message queue
 * 3. Peak energy is recalculated (+5 energy cost per quitting habit)
 *
 * The quitting process takes 30 weeks. Each week, quitProgress increments by 1.
 * After 30 weeks, the habit is removed via handleHabitChanges().
 *
 * @param player - Player object with character and message queue
 * @param habitName - Name of the habit to quit (e.g., 'smoking')
 * @returns Result object with success status and message
 */
export function quitHabit(player: Player, habitName: string): HabitOperationResult {
  const personAny = player.c as unknown as { habits?: Habit[] };

  if (!personAny.habits) {
    return {
      success: false,
      message: 'No habits found.',
    };
  }

  for (const habit of personAny.habits) {
    if (habit.name === habitName) {
      // Already quitting?
      if (habit.status === 'quitting') {
        return {
          success: false,
          message: `Already trying to quit "${formatHabitName(habitName)}".`,
          habit,
        };
      }

      // Can only quit negative habits
      if (habit.habitType !== 'negative') {
        return {
          success: false,
          message: `"${formatHabitName(habitName)}" is a positive habit and cannot be quit.`,
          habit,
        };
      }

      // Set status to quitting
      habit.status = 'quitting';
      const displayName = formatHabitName(habitName);

      // Add message to queue
      player.messageQueue = player.messageQueue ?? [];
      player.messageQueue.push(`You have decided to try quitting "${displayName}".`);

      // Recalculate peak energy (quitting habits cost +5 energy)
      getPeakEnergy(player.c);

      return {
        success: true,
        message: `Started quitting "${displayName}". This will take 30 weeks.`,
        habit,
      };
    }
  }

  return {
    success: false,
    message: `Habit "${formatHabitName(habitName)}" not found.`,
  };
}

/**
 * Stop trying to quit a habit and reset progress
 *
 * When a character gives up on quitting:
 * 1. The habit's status is reverted to 'active'
 * 2. The quitProgress is reset to 0
 * 3. A message is added to the player's message queue
 * 4. Peak energy is recalculated (no longer paying +5 energy cost)
 *
 * @param player - Player object with character and message queue
 * @param habitName - Name of the habit to stop quitting
 * @returns Result object with success status and message
 */
export function stopQuitHabit(player: Player, habitName: string): HabitOperationResult {
  const personAny = player.c as unknown as { habits?: Habit[] };

  if (!personAny.habits) {
    return {
      success: false,
      message: 'No habits found.',
    };
  }

  for (const habit of personAny.habits) {
    if (habit.name === habitName) {
      // Not currently quitting?
      if (habit.status !== 'quitting') {
        return {
          success: false,
          message: `Not currently trying to quit "${formatHabitName(habitName)}".`,
          habit,
        };
      }

      // Reset to active
      habit.status = 'active';
      habit.quitProgress = 0;
      const displayName = formatHabitName(habitName);

      // Add message to queue
      player.messageQueue = player.messageQueue ?? [];
      player.messageQueue.push(`You have decided to stop trying to quit "${displayName}".`);

      // Recalculate peak energy (no longer quitting)
      getPeakEnergy(player.c);

      return {
        success: true,
        message: `Stopped quitting "${displayName}". Progress has been reset.`,
        habit,
      };
    }
  }

  return {
    success: false,
    message: `Habit "${formatHabitName(habitName)}" not found.`,
  };
}

/**
 * Process habit quitting progress (daily tick)
 *
 * This function should be called once per in-game day.
 * For each habit with status 'quitting':
 * 1. Increment quitProgress by 1
 * 2. If quitProgress reaches 30, the habit is successfully quit:
 *    - Remove the habit from the person's habit list
 *    - Add success message to player's message queue
 *    - Recalculate peak energy
 *
 * Energy considerations:
 * - While quitting: +5 peak energy cost
 * - After successfully quitting: Energy cost is removed
 *
 * @param player - Player object for message queue
 * @param person - Person object with habits array
 * @returns Number of habits that were successfully quit this tick
 */
export function handleHabitChanges(player: Player, person: Person): number {
  const personAny = person as unknown as { habits?: Habit[] };
  let quitCount = 0;

  if (!personAny.habits) {
    return 0;
  }

  // Iterate backwards to safely remove items
  for (let i = personAny.habits.length - 1; i >= 0; i--) {
    const habit = personAny.habits[i];

    if (habit.status === 'quitting') {
      habit.quitProgress += 1;
      console.log(`Habit: ${habit.name} progress: ${habit.quitProgress}/30`);

      // Successfully quit after 30 in-game days
      if (habit.quitProgress >= 30) {
        const displayName = formatHabitName(habit.name);

        // Remove habit from list
        personAny.habits.splice(i, 1);

        // Add success message
        player.messageQueue = player.messageQueue ?? [];
        player.messageQueue.push(`You have successfully quit "${displayName}"! You feel proud and healthier.`);

        // Quitting is a real win: reward happiness + a little health so the
        // 30-day grind pays off (previously it only removed the habit + its
        // energy penalty, with no positive payoff).
        const pa = person as unknown as { happiness?: number; health?: number };
        pa.happiness = Math.min(100, (pa.happiness ?? 50) + 10);
        pa.health = Math.min(100, (pa.health ?? 100) + 5);

        // Recalculate peak energy
        getPeakEnergy(person);

        quitCount++;
      }
    }
  }

  return quitCount;
}

/**
 * Add a specific habit to a person
 *
 * @param person - Person to add the habit to
 * @param habitName - Name of the habit from positiveHabits or negativeHabits
 * @returns true if habit was added, false if already exists or not found
 */
export function addHabit(person: Person, habitName: string): boolean {
  const personAny = person as unknown as { habits?: Habit[] };
  personAny.habits = personAny.habits ?? [];

  // Check if already has this habit
  if (hasHabit(person, habitName)) {
    return false;
  }

  // Find the habit definition
  const habit = negativeHabits[habitName] ?? positiveHabits[habitName];
  if (!habit) {
    return false;
  }

  // Check for conflicting habits
  if (habit.habitType === 'negative') {
    // Adding negative habit - check if has positive counterpart
    const positiveCounterpart = habitPairs[habitName];
    if (positiveCounterpart && hasHabit(person, positiveCounterpart)) {
      // Remove the positive habit when adding negative
      removeHabit(person, positiveCounterpart);
    }
  } else {
    // Adding positive habit - check if has negative counterpart
    const negativeCounterpart = Object.entries(habitPairs).find(([_, v]) => v === habitName)?.[0];
    if (negativeCounterpart && hasHabit(person, negativeCounterpart)) {
      // Cannot add positive while having negative counterpart
      return false;
    }
  }

  // Add a copy of the habit
  personAny.habits.push({ ...habit });
  return true;
}

/**
 * Remove a specific habit from a person
 *
 * @param person - Person to remove the habit from
 * @param habitName - Name of the habit to remove
 * @returns true if habit was removed, false if not found
 */
export function removeHabit(person: Person, habitName: string): boolean {
  const personAny = person as unknown as { habits?: Habit[] };

  if (!personAny.habits) {
    return false;
  }

  const index = personAny.habits.findIndex((h) => h.name === habitName);
  if (index === -1) {
    return false;
  }

  personAny.habits.splice(index, 1);
  return true;
}

/**
 * Calculate the total energy cost from quitting habits
 *
 * @param person - Person to calculate for
 * @returns Energy cost from quitting habits (+5 per quitting habit)
 */
export function getQuittingEnergyCost(person: Person): number {
  return getQuittingHabits(person).length * 5;
}

/**
 * Get a summary of a person's habits
 *
 * @param person - Person to get summary for
 * @returns Summary object with habit counts and details
 */
export function getHabitSummary(person: Person): {
  totalHabits: number;
  positiveCount: number;
  negativeCount: number;
  quittingCount: number;
  quittingEnergyCost: number;
  habits: Habit[];
} {
  const habits = getHabits(person);
  const positiveCount = habits.filter((h) => h.habitType === 'positive').length;
  const negativeCount = habits.filter((h) => h.habitType === 'negative').length;
  const quittingCount = habits.filter((h) => h.status === 'quitting').length;

  return {
    totalHabits: habits.length,
    positiveCount,
    negativeCount,
    quittingCount,
    quittingEnergyCost: quittingCount * 5,
    habits,
  };
}

// ============================================================
// Export Manager Object
// ============================================================

/**
 * Habit manager object with all habit-related functions
 */
export const habitManager = {
  // Types (re-exported for convenience)
  // Note: Types are exported at module level

  // Habit definitions
  negativeHabits,
  positiveHabits,
  habitPairs,

  // Habit creation
  createHabit,

  // Habit assignment
  setHabits,

  // Habit querying
  getHabits,
  getNegativeHabits,
  getPositiveHabits,
  getQuittingHabits,
  hasHabit,
  findHabit,
  getHabitSummary,

  // Habit operations
  addHabit,
  removeHabit,
  quitHabit,
  stopQuitHabit,
  handleHabitChanges,

  // Energy calculations
  getQuittingEnergyCost,

  // Formatting
  formatHabitName,
};

export default habitManager;
