/**
 * RomanceSimulator - Simulates frontend romance system for headless tests
 *
 * The real game has romance handled via the frontend (swiping, dating app).
 * This simulator mimics that behavior so romance events can trigger in tests.
 */

import { Person } from '../models/Person.js';
import { Player } from '../models/Player.js';
import {
  createRelationship,
  type RomanticRelationship,
} from '../services/relationships/relationship_manager.js';

export interface RomanceSimulatorOptions {
  /** Minimum age to start dating (default: 18) */
  minDatingAge?: number;
  /** Probability of finding a match per check (default: 0.1 = 10%) */
  matchProbability?: number;
  /** Probability of progressing relationship per check (default: 0.05 = 5%) */
  progressProbability?: number;
  /** Enable logging (default: false) */
  logEvents?: boolean;
}

export interface RomanceLogEntry {
  age: number;
  event: string;
  partnerId?: string;
  partnerName?: string;
}

/**
 * Simulates the frontend romance/dating system for headless tests
 */
export class RomanceSimulator {
  private minDatingAge: number;
  private matchProbability: number;
  private progressProbability: number;
  private logEvents: boolean;
  private eventLog: RomanceLogEntry[] = [];

  constructor(options: RomanceSimulatorOptions = {}) {
    this.minDatingAge = options.minDatingAge ?? 18;
    // 20% match probability - ensures finding a partner within a few years
    this.matchProbability = options.matchProbability ?? 0.2;
    // 15% progress probability - balances test reliability with realistic pacing
    // At weekly checks, this gives ~7.8 chances per year to progress
    this.progressProbability = options.progressProbability ?? 0.15;
    this.logEvents = options.logEvents ?? false;
  }

  /**
   * Check and potentially create/progress romance for player
   * Should be called periodically (e.g., weekly in game time)
   */
  tick(player: Player): void {
    const age = player.character.ageYears;

    // Only process if old enough
    if (age < this.minDatingAge) return;

    // If no partner, try to find one
    if (!player.character.partner) {
      this.tryFindMatch(player);
    } else {
      // If has partner, maybe progress relationship
      this.tryProgressRelationship(player);
    }
  }

  /**
   * Simulate finding a dating match (like frontend swiping)
   */
  private tryFindMatch(player: Player): void {
    // Check probability
    if (Math.random() > this.matchProbability) return;

    // Look for existing dating_match or crush in relationships
    let potentialPartner = player.r.find(
      (p) =>
        p.relationships.includes('dating_match') ||
        p.relationships.includes('Crush') ||
        (p.affinity >= 50 && !p.relationships.includes('Parent') && !p.relationships.includes('Sibling'))
    );

    // If no potential partner found, create one
    if (!potentialPartner) {
      potentialPartner = this.createDatingMatch(player);
      player.r.push(potentialPartner);
    }

    // Set as partner
    (player.character as unknown as { partner: string }).partner = potentialPartner.id;

    // Ensure high enough affinity for romance events
    if (potentialPartner.affinity < 50) {
      potentialPartner.affinity = 50 + Math.floor(Math.random() * 30); // 50-80
    }

    // Create relationship data if doesn't exist
    const existingRel = player.relData.find((r) => {
      const rel = r as unknown as RomanticRelationship;
      return rel.person2 === potentialPartner!.id;
    });

    if (!existingRel) {
      const relationship = createRelationship(
        player.c.id,
        potentialPartner.id,
        player.date,
        'Dating',
        'Started dating.'
      );
      player.relData.push(relationship as unknown as typeof player.relData[0]);
    }

    // Add dating_match if not present
    if (!potentialPartner.relationships.includes('dating_match')) {
      potentialPartner.relationships.push('dating_match');
    }

    // Happiness boost from finding love
    player.character.happiness = Math.min(100, (player.character.happiness ?? 50) + 15);

    this.log({
      age: player.character.ageYears,
      event: 'found_match',
      partnerId: potentialPartner.id,
      partnerName: `${potentialPartner.firstname} ${potentialPartner.lastname}`,
    });
  }

  /**
   * Try to progress an existing relationship (engagement, marriage)
   */
  private tryProgressRelationship(player: Player): void {
    if (Math.random() > this.progressProbability) return;

    const partnerId = player.character.partner;
    if (!partnerId) return;

    // Find the relationship data
    const relData = player.relData.find((r) => {
      const rel = r as unknown as RomanticRelationship;
      return rel.person2 === partnerId;
    }) as unknown as RomanticRelationship | undefined;

    if (!relData) return;

    // Progress based on current status
    if (relData.relationshipStatus === 'Dating' && player.character.ageYears >= 20) {
      // Get engaged
      relData.relationshipStatus = 'Engaged';
      (player.character as unknown as { engaged: boolean }).engaged = true;
      relData.eventsLog.push('Got engaged!');

      player.character.happiness = Math.min(100, (player.character.happiness ?? 50) + 20);

      this.log({
        age: player.character.ageYears,
        event: 'engaged',
        partnerId,
      });
    } else if (relData.relationshipStatus === 'Engaged' && player.character.ageYears >= 21) {
      // Get married
      relData.relationshipStatus = 'Married';
      (player.character as unknown as { married: boolean }).married = true;
      relData.eventsLog.push('Got married!');

      // Update partner relationship type
      const partner = player.r.find((p) => p.id === partnerId);
      if (partner) {
        if (!partner.relationships.includes('Spouse')) {
          partner.relationships.push('Spouse');
        }
      }

      player.character.happiness = Math.min(100, (player.character.happiness ?? 50) + 25);

      this.log({
        age: player.character.ageYears,
        event: 'married',
        partnerId,
      });
    }
  }

  /**
   * Create a new dating match character
   */
  private createDatingMatch(player: Player): Person {
    const playerAge = player.character.ageYears;
    const isMale = Math.random() > 0.5;

    const maleNames = ['Michael', 'David', 'James', 'Robert', 'William', 'Daniel', 'Matthew', 'Christopher'];
    const femaleNames = ['Emily', 'Sarah', 'Jessica', 'Ashley', 'Emma', 'Olivia', 'Sophia', 'Isabella'];
    const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'Wilson'];

    const names = isMale ? maleNames : femaleNames;
    const firstname = names[Math.floor(Math.random() * names.length)];
    const lastname = lastNames[Math.floor(Math.random() * lastNames.length)];

    // Age within 5 years of player
    const ageOffset = Math.floor(Math.random() * 11) - 5; // -5 to +5
    const partnerAge = Math.max(18, playerAge + ageOffset);

    return new Person({
      id: `dating-match-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
      firstname,
      lastname,
      sex: isMale ? 'Male' : 'Female',
      ageYears: partnerAge,
      ageDays: partnerAge * 365,
      status: 'alive',
      relationships: ['dating_match'],
      affinity: 60 + Math.floor(Math.random() * 30), // 60-90 affinity
      familiarity: 40 + Math.floor(Math.random() * 30), // 40-70 familiarity
    });
  }

  /**
   * Force a dating match (useful for testing specific scenarios)
   */
  forceMatch(player: Player): Person {
    const partner = this.createDatingMatch(player);
    player.r.push(partner);

    (player.character as unknown as { partner: string }).partner = partner.id;

    const relationship = createRelationship(
      player.c.id,
      partner.id,
      player.date,
      'Dating',
      'Started dating.'
    );
    player.relData.push(relationship as unknown as typeof player.relData[0]);

    player.character.happiness = Math.min(100, (player.character.happiness ?? 50) + 15);

    this.log({
      age: player.character.ageYears,
      event: 'forced_match',
      partnerId: partner.id,
      partnerName: `${partner.firstname} ${partner.lastname}`,
    });

    return partner;
  }

  /**
   * Log an event
   */
  private log(entry: RomanceLogEntry): void {
    this.eventLog.push(entry);
    if (this.logEvents) {
      console.log(`[RomanceSimulator] Age ${entry.age}: ${entry.event}`, entry.partnerName || '');
    }
  }

  /**
   * Get the event log
   */
  getEventLog(): RomanceLogEntry[] {
    return [...this.eventLog];
  }

  /**
   * Clear the event log
   */
  clearLog(): void {
    this.eventLog = [];
  }
}
