/**
 * Messaging Style System
 *
 * This module provides dynamic, personality-driven messaging styles for NPC conversations.
 * Characters have base messaging traits that are modified per-relationship and by mood states.
 * Ported from Python messaging_style.py
 *
 * See docs/plans/2025-11-13-dynamic-messaging-styles-design.md for full design documentation.
 */

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

// ============================================================================
// Types
// ============================================================================

export interface MessagingTraits {
  verbosity: number; // 0-100 - how long messages tend to be
  inquisitiveness: number; // 0-100 - how often asks questions
  expressiveness: number; // 0-100 - how animated/enthusiastic
  responsiveness: number; // 0-100 - how fully engages with messages
  openness: number; // 0-100 - how much shares personal details
  emoji_usage: number; // 0-100 - how often uses emojis
  formality: number; // 0-100 - how formal/proper the language
  response_timing: number; // 0-100 - how quickly responds
}

export interface MessagingPatterns {
  time_of_day_preference: 'morning' | 'night' | 'neutral';
  weekend_availability: number; // 0-100
  typing_style: 'proper' | 'casual' | 'chaotic';
}

export type MoodState = 'great' | 'bad' | 'stressed' | 'neutral';

export const VALID_MOODS: readonly MoodState[] = ['great', 'bad', 'stressed', 'neutral'] as const;

/**
 * Messaging modifiers for a relationship.
 * These are applied on top of the character's base traits.
 * All trait modifiers are optional and default to 0 if not set.
 */
export interface MessagingModifiers {
  // Permanent/evolving modifiers (optional to match Player.ts)
  verbosity?: number;
  inquisitiveness?: number;
  expressiveness?: number;
  responsiveness?: number;
  openness?: number;
  emoji_usage?: number;
  formality?: number;
  response_timing?: number;

  // Contextual state
  mood_state?: MoodState;
  current_topic_engagement?: number;
}

export interface Relationship {
  messaging_modifiers?: MessagingModifiers;
  characterId?: string;
}

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

/**
 * Get relationship data for a character by their ID.
 * Returns the messaging modifiers object for that character.
 */
export function getRelationshipData(player: Player, characterId: string): MessagingModifierData | null {
  if (!player.messagingModifiers) {
    return null;
  }
  return player.messagingModifiers.find((r) => r.characterId === characterId) ?? null;
}

/**
 * Ensure a character has relationship data initialized.
 * Creates new relationship data with messaging modifiers if it doesn't exist.
 */
export function ensureRelationshipData(
  player: Player,
  character: Person
): MessagingModifierData {
  let relData = getRelationshipData(player, character.id);

  if (!relData) {
    relData = {
      characterId: character.id,
      messaging_modifiers: initializeRelationshipModifiers(character),
    };

    if (!player.messagingModifiers) {
      player.messagingModifiers = [];
    }
    player.messagingModifiers.push(relData);
  }

  // Ensure messaging_modifiers exists
  if (!relData.messaging_modifiers) {
    relData.messaging_modifiers = initializeRelationshipModifiers(character);
  }

  return relData;
}

// ============================================================================
// Initialization Functions
// ============================================================================

/**
 * Generate random base messaging traits for a new character.
 */
export function initializeMessagingTraits(): MessagingTraits {
  return {
    verbosity: Math.floor(Math.random() * 41) + 30, // 30-70: Most people are moderately verbose
    inquisitiveness: Math.floor(Math.random() * 61) + 20, // 20-80
    expressiveness: Math.floor(Math.random() * 61) + 20, // 20-80
    responsiveness: Math.floor(Math.random() * 41) + 40, // 40-80: Most people are at least somewhat responsive
    openness: Math.floor(Math.random() * 41) + 20, // 20-60: Most start somewhat guarded
    emoji_usage: Math.floor(Math.random() * 81) + 10, // 10-90
    formality: Math.floor(Math.random() * 61) + 20, // 20-80
    response_timing: Math.floor(Math.random() * 46) + 40, // 40-85
  };
}

/**
 * Generate random messaging patterns for a new character.
 */
export function initializeMessagingPatterns(): MessagingPatterns {
  const preferences: Array<'morning' | 'night' | 'neutral'> = ['morning', 'night', 'neutral'];
  const styles: Array<'proper' | 'casual' | 'chaotic'> = ['proper', 'casual', 'chaotic'];

  return {
    time_of_day_preference: preferences[Math.floor(Math.random() * preferences.length)],
    weekend_availability: Math.floor(Math.random() * 61) + 30, // 30-90
    typing_style: styles[Math.floor(Math.random() * styles.length)],
  };
}

/**
 * Initialize messaging modifiers for a new relationship.
 * Can apply relationship-type-specific starting modifiers.
 */
export function initializeRelationshipModifiers(character?: Person): MessagingModifiers {
  const modifiers: MessagingModifiers = {
    // Permanent/evolving modifiers
    verbosity: 0,
    inquisitiveness: 0,
    expressiveness: 0,
    responsiveness: 0,
    openness: 0,
    emoji_usage: 0,
    formality: 0,
    response_timing: 0,

    // Contextual state
    mood_state: 'neutral',
    current_topic_engagement: 0,
  };

  // Apply relationship-type-specific starting modifiers if character provided
  if (character?.relationships?.length) {
    const primaryRelationship = character.relationships[0].toLowerCase();

    // Boss/professional: start more formal
    if (['boss', 'teacher', 'coworker'].includes(primaryRelationship)) {
      modifiers.formality = (modifiers.formality ?? 0) + 20;
      modifiers.emoji_usage = (modifiers.emoji_usage ?? 0) - 15;
      modifiers.openness = (modifiers.openness ?? 0) - 10;
    }
    // Romantic: start more expressive
    else if (['partner', 'girlfriend', 'boyfriend', 'spouse'].includes(primaryRelationship)) {
      modifiers.expressiveness = (modifiers.expressiveness ?? 0) + 15;
      modifiers.emoji_usage = (modifiers.emoji_usage ?? 0) + 10;
      modifiers.openness = (modifiers.openness ?? 0) + 10;
    }
    // Family: start more casual
    else if (['mother', 'father', 'sibling'].includes(primaryRelationship)) {
      modifiers.formality = (modifiers.formality ?? 0) - 15;
      modifiers.openness = (modifiers.openness ?? 0) + 5;
    }
  }

  return modifiers;
}

// ============================================================================
// Trait Calculation
// ============================================================================

interface MoodEffects {
  [trait: string]: number;
}

const MOOD_EFFECTS: Record<MoodState, MoodEffects> = {
  great: {
    expressiveness: 15,
    verbosity: 10,
    emoji_usage: 20,
    inquisitiveness: 10,
    responsiveness: 10,
  },
  bad: {
    expressiveness: -20,
    responsiveness: -15,
    verbosity: -10,
    openness: -10,
    emoji_usage: -15,
  },
  stressed: {
    responsiveness: -25,
    verbosity: 20, // Rambles when stressed
    openness: -10,
    formality: -10, // More casual/sloppy when stressed
  },
  neutral: {},
};

/**
 * Get temporary mood-based adjustment for a specific trait.
 */
export function getMoodModifier(moodState: MoodState, trait: string): number {
  return MOOD_EFFECTS[moodState]?.[trait] ?? 0;
}

/**
 * Calculate final trait values: base + relationship modifier + mood modifier.
 */
export function calculateEffectiveTraits(
  character: Person & { messaging_traits?: MessagingTraits },
  relationship?: Relationship
): MessagingTraits {
  // Character doesn't have traits yet, return defaults
  if (!character.messaging_traits) {
    return initializeMessagingTraits();
  }

  const base = character.messaging_traits;
  const effective: MessagingTraits = { ...base };

  // Get relationship modifiers if available
  const relMods = relationship?.messaging_modifiers;
  const moodState = relMods?.mood_state ?? 'neutral';

  // Calculate effective value for each trait
  for (const trait of Object.keys(base) as Array<keyof MessagingTraits>) {
    const baseValue = base[trait] ?? 50;
    const relationshipModifier = typeof relMods?.[trait] === 'number' ? relMods[trait] : 0;
    const moodModifier = getMoodModifier(moodState, trait);

    const effectiveValue = baseValue + relationshipModifier + moodModifier;
    effective[trait] = Math.max(0, Math.min(100, effectiveValue)); // Clamp 0-100
  }

  return effective;
}

// ============================================================================
// Prompt Generation
// ============================================================================

/**
 * Generate natural language style instructions for AI prompt based on calculated traits.
 */
export function getMessagingStylePrompt(
  character: Person & { messaging_traits?: MessagingTraits },
  relationship?: Relationship
): string {
  const effectiveTraits = calculateEffectiveTraits(character, relationship);
  const instructions: string[] = [];

  // Verbosity
  if (effectiveTraits.verbosity < 30) {
    instructions.push(
      'Keep responses extremely brief - 1-2 short sentences max, often just a few words.'
    );
  } else if (effectiveTraits.verbosity > 70) {
    instructions.push(
      "You tend to ramble and share lots of details. Don't hold back - elaborate freely."
    );
  }

  // Inquisitiveness
  if (effectiveTraits.inquisitiveness < 30) {
    instructions.push(
      'You rarely ask questions. Just respond without pushing the conversation forward.'
    );
  } else if (effectiveTraits.inquisitiveness > 70) {
    instructions.push(
      "You're naturally curious - frequently ask follow-up questions and show genuine interest."
    );
  }

  // Expressiveness
  if (effectiveTraits.expressiveness < 30) {
    instructions.push('Keep tone neutral and understated. No excitement or emphasis.');
  } else if (effectiveTraits.expressiveness > 70) {
    instructions.push(
      'Be animated and enthusiastic! Use exclamation marks, show excitement, be expressive.'
    );
  }

  // Responsiveness
  if (effectiveTraits.responsiveness < 30) {
    instructions.push(
      "Give minimal effort responses. Don't build on topics or match their energy."
    );
  } else if (effectiveTraits.responsiveness > 70) {
    instructions.push(
      'Engage fully with what they say. Build on their points, match their energy.'
    );
  }

  // Openness
  if (effectiveTraits.openness < 30) {
    instructions.push('Stay surface-level. Avoid sharing personal details or deep thoughts.');
  } else if (effectiveTraits.openness > 70) {
    instructions.push(
      'Feel free to share personal details, thoughts, and feelings openly - even TMI sometimes.'
    );
  }

  // Emoji usage
  if (effectiveTraits.emoji_usage < 20) {
    instructions.push('Never use emojis.');
  } else if (effectiveTraits.emoji_usage >= 40 && effectiveTraits.emoji_usage < 70) {
    instructions.push('Use an emoji occasionally when it fits (0-1 per message).');
  } else if (effectiveTraits.emoji_usage >= 70) {
    instructions.push(
      'Use emojis frequently to express yourself (2-4 per message is normal for you).'
    );
  }

  // Formality
  if (effectiveTraits.formality < 30) {
    instructions.push(
      'Text casually - lowercase, abbreviations (lol, idk, ur), minimal punctuation.'
    );
  } else if (effectiveTraits.formality > 70) {
    instructions.push(
      "Use proper grammar, spelling, and punctuation. Text like you're writing an email."
    );
  }

  return instructions.join(' ');
}

// ============================================================================
// Modifier Updates
// ============================================================================

/**
 * Update messaging modifiers based on conversation dynamics.
 * Called after each AI response to evolve the relationship's messaging style.
 */
export function updateConversationMessagingModifiers(
  relationship: Relationship,
  sentiment: 'positive' | 'negative' | 'neutral',
  conversationLength: number,
  topicEngagement = 0
): void {
  if (!relationship?.messaging_modifiers) {
    return;
  }

  const mods = relationship.messaging_modifiers;

  // Clamp topic_engagement to valid range
  topicEngagement = Math.max(-30, Math.min(30, topicEngagement));

  // Positive conversations increase openness, expressiveness, responsiveness
  if (sentiment === 'positive') {
    mods.openness = (mods.openness ?? 0) + 1;
    mods.expressiveness = (mods.expressiveness ?? 0) + 1;
    mods.responsiveness = (mods.responsiveness ?? 0) + 1;
  }
  // Negative conversations decrease engagement
  else if (sentiment === 'negative') {
    mods.responsiveness = (mods.responsiveness ?? 0) - 3;
    mods.openness = (mods.openness ?? 0) - 2;
    mods.expressiveness = (mods.expressiveness ?? 0) - 2;
  }
  // Neutral conversations have small positive drift
  else {
    mods.responsiveness = (mods.responsiveness ?? 0) + 0.5;
  }

  // Long conversations increase comfort and casualness
  if (conversationLength > 20) {
    mods.verbosity = (mods.verbosity ?? 0) + 1;
    mods.formality = (mods.formality ?? 0) - 1;
    mods.openness = (mods.openness ?? 0) + 1;
  }

  // Very long conversations increase even more
  if (conversationLength > 50) {
    mods.formality = (mods.formality ?? 0) - 2;
    mods.openness = (mods.openness ?? 0) + 2;
  }

  // Update topic engagement (resets each conversation)
  mods.current_topic_engagement = topicEngagement;

  // Cap all modifiers at -40 to +40
  const numericKeys = [
    'verbosity',
    'inquisitiveness',
    'expressiveness',
    'responsiveness',
    'openness',
    'emoji_usage',
    'formality',
    'response_timing',
    'current_topic_engagement',
  ] as const;

  for (const key of numericKeys) {
    const val = mods[key];
    if (typeof val === 'number') {
      mods[key] = Math.max(-40, Math.min(40, val));
    }
  }
}

/**
 * Set character's mood state across all their relationships.
 * Mood affects all conversations with this character.
 */
export function setMoodState(
  character: Person,
  player: Player,
  mood: MoodState
): void {
  // Validate mood value
  if (!VALID_MOODS.includes(mood)) {
    console.warn(`Invalid mood state '${mood}', defaulting to 'neutral'`);
    mood = 'neutral';
  }

  // Find relationship with this character using helper function
  const relationship = getRelationshipData(player, character.id);

  if (relationship?.messaging_modifiers) {
    relationship.messaging_modifiers.mood_state = mood;
    console.log(`Set ${character.firstname}'s mood to '${mood}'`);
  }
}

/**
 * Gradually return messaging modifiers toward neutral (0).
 * Called periodically (weekly recommended) to prevent permanent extremes.
 */
export function decayMessagingModifiers(relationship: Relationship, decayRate = 0.1): void {
  if (!relationship?.messaging_modifiers) {
    return;
  }

  const mods = relationship.messaging_modifiers;
  const numericKeys = [
    'verbosity',
    'inquisitiveness',
    'expressiveness',
    'responsiveness',
    'openness',
    'emoji_usage',
    'formality',
    'response_timing',
  ] as const;

  for (const trait of numericKeys) {
    const val = mods[trait];
    if (typeof val === 'number') {
      // Move toward 0
      let newValue = val * (1 - decayRate);

      // Round to 0 if very close
      if (Math.abs(newValue) < 0.5) {
        newValue = 0;
      }

      mods[trait] = newValue;
    }
  }
}

// ============================================================================
// Event Modifiers
// ============================================================================

type EventType =
  | 'start_dating'
  | 'breakup'
  | 'become_best_friends'
  | 'major_fight'
  | 'got_promoted'
  | 'lost_job'
  | 'parent_died'
  | 'had_baby';

interface EventEffect {
  mood_state?: MoodState;
  [trait: string]: number | MoodState | undefined;
}

const EVENT_EFFECTS: Record<EventType, EventEffect> = {
  // Relationship milestones
  start_dating: {
    openness: 15,
    expressiveness: 10,
    emoji_usage: 10,
    inquisitiveness: 10,
  },
  breakup: {
    responsiveness: -40,
    openness: -30,
    emoji_usage: -20,
    expressiveness: -25,
  },
  become_best_friends: {
    openness: 20,
    responsiveness: 15,
    formality: -15,
    emoji_usage: 10,
  },
  major_fight: {
    responsiveness: -25,
    openness: -15,
    expressiveness: -10,
    formality: 5,
  },

  // Life events (affect mood more than permanent modifiers)
  got_promoted: {
    mood_state: 'great',
  },
  lost_job: {
    mood_state: 'stressed',
  },
  parent_died: {
    mood_state: 'bad',
    responsiveness: -15,
    openness: -10,
  },
  had_baby: {
    mood_state: 'great',
    verbosity: 10, // New parents talk a lot about their baby
  },
};

/**
 * Apply messaging modifier changes for major relationship/life events.
 */
export function applyEventModifiers(relationship: Relationship, eventType: EventType): void {
  if (!relationship?.messaging_modifiers) {
    return;
  }

  const effects = EVENT_EFFECTS[eventType];
  if (!effects) {
    console.warn(`Unknown event type '${eventType}' for messaging modifiers`);
    return;
  }

  const mods = relationship.messaging_modifiers;
  const numericKeys = [
    'verbosity',
    'inquisitiveness',
    'expressiveness',
    'responsiveness',
    'openness',
    'emoji_usage',
    'formality',
    'response_timing',
  ] as const;

  for (const [key, value] of Object.entries(effects)) {
    if (key === 'mood_state') {
      mods.mood_state = value as MoodState;
    } else if (typeof value === 'number') {
      // Handle each numeric key explicitly
      const numKey = key as (typeof numericKeys)[number];
      if (numericKeys.includes(numKey as (typeof numericKeys)[number])) {
        const currentVal = mods[numKey];
        if (typeof currentVal === 'number') {
          mods[numKey] = currentVal + value;
        }
      }
    }
  }

  // Cap modifiers
  for (const key of numericKeys) {
    const val = mods[key];
    if (typeof val === 'number') {
      mods[key] = Math.max(-40, Math.min(40, val));
    }
  }

  console.log(`Applied '${eventType}' messaging modifiers:`, effects);
}

// ============================================================================
// Debug
// ============================================================================

export interface DebugMessagingInfo {
  character_name: string;
  base_traits: MessagingTraits | undefined;
  relationship_modifiers: MessagingModifiers | undefined;
  effective_traits: MessagingTraits;
  mood_state: MoodState;
  prompt_instructions: string;
}

/**
 * Get detailed debug information about a character's messaging style.
 * Useful for testing and debugging.
 */
export function getDebugMessagingInfo(
  character: Person & { messaging_traits?: MessagingTraits },
  relationship?: Relationship
): DebugMessagingInfo {
  const base = character.messaging_traits;
  const mods = relationship?.messaging_modifiers;
  const effective = calculateEffectiveTraits(character, relationship);

  return {
    character_name: `${character.firstname} ${character.lastname}`,
    base_traits: base,
    relationship_modifiers: mods,
    effective_traits: effective,
    mood_state: mods?.mood_state ?? 'neutral',
    prompt_instructions: getMessagingStylePrompt(character, relationship),
  };
}

// ============================================================================
// Daily Mood Updates
// ============================================================================

/**
 * Update a character's mood based on their current life situation.
 * Called daily to reflect changes in stress and happiness.
 */
export function updateCharacterMood(
  character: Person,
  player: Player
): MoodState {
  let newMood: MoodState = 'neutral';

  // Determine mood based on character stats
  const stress = character.stress ?? 0;
  const happiness = character.happiness ?? 50;

  if (stress > 70) {
    newMood = 'stressed';
  } else if (happiness > 80) {
    newMood = 'great';
  } else if (happiness < 30) {
    newMood = 'bad';
  } else {
    newMood = 'neutral';
  }

  // Apply the mood state
  setMoodState(character, player, newMood);

  return newMood;
}

/**
 * Update mood states for all characters based on their life situations.
 * Should be called daily during the game loop.
 */
export function updateDailyMoods(player: Player): void {
  if (!player.r || !Array.isArray(player.r)) {
    return;
  }

  for (const character of player.r) {
    updateCharacterMood(character, player);
  }
}

// ============================================================================
// Ensure Character Has Messaging Traits
// ============================================================================

/**
 * Ensure a character has messaging traits initialized.
 * If they don't have traits, initialize them with random values.
 */
export function ensureMessagingTraits(
  character: Person & { messaging_traits?: MessagingTraits; messaging_patterns?: MessagingPatterns }
): void {
  if (!character.messaging_traits) {
    character.messaging_traits = initializeMessagingTraits();
  }
  if (!character.messaging_patterns) {
    character.messaging_patterns = initializeMessagingPatterns();
  }
}

/**
 * Get messaging traits for a character, initializing if needed.
 */
export function getMessagingTraits(
  character: Person & { messaging_traits?: MessagingTraits }
): MessagingTraits {
  if (!character.messaging_traits) {
    character.messaging_traits = initializeMessagingTraits();
  }
  return character.messaging_traits;
}
