/**
 * Random Relationship Events
 * Ported from Python relationships/relationship_manager.py handleRelationships()
 *
 * These events have a 5% chance of triggering each week for romantic relationships:
 * - Went on a romantic trip (+10 relationship score)
 * - Had a disagreement (-10 relationship score)
 * - Met with friends (+5 relationship score)
 */

import { Player } from '../../models/Player.js';
import { Person } from '../../models/Person.js';
import { createMessageEvent, EventResult } from '../base.js';
import {
  getActiveRelationship,
  RomanticRelationship,
  getRelData,
} from '../../services/relationships/relationship_manager.js';

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

export interface RandomRelationshipEvent {
  name: string;
  message: string;
  scoreChange: number;
  affinityChange: number;
  image?: string;
  positive: boolean;
}

// ============================================================================
// Random Event Definitions
// ============================================================================

const RANDOM_RELATIONSHIP_EVENTS: RandomRelationshipEvent[] = [
  {
    name: 'romantic_trip',
    message: 'You went on a romantic trip with {partnerName}. It was wonderful!',
    scoreChange: 10,
    affinityChange: 5,
    image: 'https://lichun.app/assets/images/romanticTrip.png',
    positive: true,
  },
  {
    name: 'disagreement',
    message: 'You and {partnerName} had a disagreement. Things are a bit tense.',
    scoreChange: -10,
    affinityChange: -3,
    image: 'https://lichun.app/assets/images/disagreement.png',
    positive: false,
  },
  {
    name: 'met_friends',
    message: 'You and {partnerName} met with mutual friends. It was a fun time!',
    scoreChange: 5,
    affinityChange: 2,
    image: 'https://lichun.app/assets/images/friendsMeeting.png',
    positive: true,
  },
  {
    name: 'surprise_date',
    message: '{partnerName} surprised you with a special date night!',
    scoreChange: 8,
    affinityChange: 4,
    image: 'https://lichun.app/assets/images/surpriseDate.png',
    positive: true,
  },
  {
    name: 'cozy_night',
    message: 'You and {partnerName} had a cozy night in watching movies.',
    scoreChange: 3,
    affinityChange: 2,
    image: 'https://lichun.app/assets/images/cozyNight.png',
    positive: true,
  },
  {
    name: 'miscommunication',
    message: 'There was a miscommunication with {partnerName}, but you worked through it.',
    scoreChange: -3,
    affinityChange: 0,
    image: 'https://lichun.app/assets/images/miscommunication.png',
    positive: false,
  },
  {
    name: 'shared_hobby',
    message: 'You discovered a new shared hobby with {partnerName}!',
    scoreChange: 6,
    affinityChange: 3,
    image: 'https://lichun.app/assets/images/sharedHobby.png',
    positive: true,
  },
  {
    name: 'jealousy_moment',
    message: '{partnerName} seemed a bit jealous today, but you reassured them.',
    scoreChange: -2,
    affinityChange: 1,
    image: 'https://lichun.app/assets/images/jealousy.png',
    positive: false,
  },
];

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

/**
 * Get a random event from the pool
 */
function getRandomEvent(): RandomRelationshipEvent {
  const index = Math.floor(Math.random() * RANDOM_RELATIONSHIP_EVENTS.length);
  return RANDOM_RELATIONSHIP_EVENTS[index];
}

/**
 * Get partner person from player relationships
 */
function getPartnerPerson(player: Player, relationship: RomanticRelationship): Person | null {
  const partnerId = relationship.person2;
  return player.r.find((p) => p.id === partnerId) ?? null;
}

// ============================================================================
// Main Functions
// ============================================================================

/**
 * Handle random relationship events for romantic relationships
 * Ported from Python handleRelationships() in relationship_manager.py
 *
 * This function:
 * - Has a 5% chance of triggering random relationship events
 * - Updates relationship score based on event
 * - Logs events to relationship eventsLog
 * - Applies correction mechanism to keep scores balanced
 *
 * @param player - The player object
 * @param person - The partner person to check relationship with
 * @returns EventResult if an event was triggered, null otherwise
 */
export function handleRandomRelationshipEvents(
  player: Player,
  person: Person
): EventResult {
  const eventChance = Math.floor(Math.random() * 100) + 1;
  const rel = getRelData(player, person.id);

  if (!rel) {
    return null;
  }

  // Only process for active romantic relationships
  if (rel.relationshipStatus !== 'Dating' && rel.relationshipStatus !== 'Married') {
    return null;
  }

  let eventResult: EventResult = null;

  // 5% chance of random event (event_chance > 95)
  if (eventChance > 95) {
    const event = getRandomEvent();
    const partner = getPartnerPerson(player, rel);
    const partnerName = partner
      ? `${partner.firstname} ${partner.lastname}`
      : 'your partner';

    // Format message with partner name
    const formattedMessage = event.message.replace('{partnerName}', partnerName);

    // Log event to relationship
    rel.eventsLog.push(formattedMessage);

    // Update relationship score
    rel.relationshipScore += event.scoreChange;

    // Update partner affinity if partner exists
    if (partner) {
      partner.affinity = Math.max(-100, Math.min(100, (partner.affinity ?? 0) + event.affinityChange));
    }

    // Create event result
    eventResult = createMessageEvent(
      `relationship_${event.name}`,
      formattedMessage,
      player,
      true,
      {
        title: event.positive ? 'Relationship Moment' : 'Relationship Challenge',
        image: event.image,
        affinityChange: event.affinityChange,
      }
    );

    if (process.env.NODE_ENV !== 'test') {
      console.log(
        `Random relationship event: ${event.name} for ${partnerName}, ` +
        `score change: ${event.scoreChange}, affinity change: ${event.affinityChange}`
      );
    }
  }

  // Regular weekly relationship score update (+1)
  rel.relationshipScore += 1;

  // Correction mechanism to keep scores balanced around 50
  // Ported from Python: if score < 40 add 2, if > 60 subtract 2
  if (rel.relationshipScore < 40) {
    rel.relationshipScore += 2;
  } else if (rel.relationshipScore > 60) {
    rel.relationshipScore -= 2;
  }

  // Clamp relationship score to valid range
  rel.relationshipScore = Math.max(0, Math.min(100, rel.relationshipScore));

  return eventResult;
}

/**
 * Process all romantic relationships for random events
 * Should be called weekly from the game loop
 *
 * @param player - The player object
 * @returns Array of event results for any triggered events
 */
export function processWeeklyRelationshipEvents(player: Player): EventResult[] {
  const events: EventResult[] = [];

  // Get active romantic relationship
  const relationship = getActiveRelationship(player);

  if (relationship) {
    // Find the partner person
    const partner = player.r.find((p) => p.id === relationship.person2);

    if (partner && partner.status === 'alive') {
      const result = handleRandomRelationshipEvents(player, partner);
      if (result) {
        events.push(result);
      }
    }
  }

  return events;
}

/**
 * Check if player has an active romantic relationship
 */
export function hasActiveRomance(player: Player): boolean {
  const rel = getActiveRelationship(player);
  return rel !== null && (rel.relationshipStatus === 'Dating' || rel.relationshipStatus === 'Married');
}

/**
 * Get relationship health status
 * Returns a descriptor based on relationship score
 */
export function getRelationshipHealth(player: Player): string | null {
  const rel = getActiveRelationship(player);

  if (!rel) {
    return null;
  }

  const score = rel.relationshipScore;

  if (score >= 80) return 'Thriving';
  if (score >= 60) return 'Healthy';
  if (score >= 40) return 'Stable';
  if (score >= 20) return 'Struggling';
  return 'Critical';
}

/**
 * Get all random event definitions (for testing/display)
 */
export function getAllRandomEvents(): RandomRelationshipEvent[] {
  return [...RANDOM_RELATIONSHIP_EVENTS];
}

// ============================================================================
// Export
// ============================================================================

/**
 * Only actual event functions that return EventResult.
 * Helper functions (processWeeklyRelationshipEvents, hasActiveRomance,
 * getRelationshipHealth, getAllRandomEvents) are exported individually
 * but NOT included here, as they return non-event values (arrays, booleans,
 * strings) and would corrupt allEvents when spread into it.
 */
export const randomRelationshipEvents = {
  handleRandomRelationshipEvents,
};

// ============================================================================
// Friend & family weekly beats (T9 — make social life beyond romance matter)
// ============================================================================

/**
 * Small ambient events for NON-romantic relationships (friends, family, your
 * own children). Previously the only weekly social events were partner-only, so
 * friend/family NPCs were inert flavor — their affinity never moved. These give
 * the wider social circle the occasional beat so those relationships evolve.
 */
const FRIEND_EVENTS: RandomRelationshipEvent[] = [
  { name: 'caught_up', message: 'You caught up with {name} over coffee. Good to reconnect.', scoreChange: 3, affinityChange: 3, positive: true },
  { name: 'helped_out', message: 'You helped {name} through a busy week. They really appreciated it.', scoreChange: 4, affinityChange: 4, positive: true },
  { name: 'hobby_together', message: 'You and {name} spent an afternoon on a shared hobby.', scoreChange: 3, affinityChange: 3, positive: true },
  { name: 'celebrated', message: 'You celebrated some good news with {name}.', scoreChange: 4, affinityChange: 4, positive: true },
  { name: 'favor', message: '{name} did you a kind favor out of the blue.', scoreChange: 3, affinityChange: 3, positive: true },
  { name: 'lost_touch', message: "You realized you haven't talked to {name} in a while - you've drifted a little.", scoreChange: -2, affinityChange: -2, positive: false },
  { name: 'small_disagreement', message: 'You and {name} had a small disagreement, but talked it through.', scoreChange: -1, affinityChange: -1, positive: false },
];

const FRIEND_TAGS = new Set([
  'friend', 'best friend', 'bestfriend', 'bff', 'family', 'child', 'sibling', 'parent',
]);

/**
 * Once per in-game week, maybe fire ONE small beat for a non-romantic
 * relationship (friend/family/child), nudging that specific NPC's affinity.
 * Gentle cadence (~12%/week) so it adds texture without spam. Called from both
 * the online (PlayerSession) and offline (GameEngine) weekly ticks alongside
 * the romantic processWeeklyRelationshipEvents.
 */
export function processWeeklyFriendEvents(player: Player): EventResult[] {
  const events: EventResult[] = [];

  // Exclude the active romantic partner (handled by the romance pass).
  const activeRel = getActiveRelationship(player);
  const partnerId = activeRel?.person2;

  const circle = (player.r ?? []).filter(
    (p) =>
      p.status === 'alive' &&
      p.id !== partnerId &&
      (p.relationships ?? []).some((t) => FRIEND_TAGS.has(t.toLowerCase()))
  );
  if (circle.length === 0) return events;

  // ~12% chance of a single beat per week.
  if (Math.random() > 0.12) return events;

  const friend = circle[Math.floor(Math.random() * circle.length)];
  const event = FRIEND_EVENTS[Math.floor(Math.random() * FRIEND_EVENTS.length)];
  const name = friend.firstname || 'a friend';
  const message = event.message.replace('{name}', name);

  friend.affinity = Math.max(-100, Math.min(100, (friend.affinity ?? 0) + event.affinityChange));

  const result = createMessageEvent(
    `friend_${event.name}_${friend.id}`,
    message,
    player,
    true,
    {
      title: event.affinityChange >= 0 ? 'Friendship' : 'Friendship',
      affinityChange: event.affinityChange,
    }
  );
  if (result) events.push(result);
  return events;
}
