/**
 * Relationship Events System
 *
 * Handles dynamic relationship events like arguments, anniversaries,
 * jealousy, proposals, and breakup threats. Events trigger based on
 * relationship conditions and offer resolution options with affinity
 * and diamond costs.
 */

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

export interface RelationshipEventOption {
  choice: string;
  label: string;
  affinityChange: number;
  diamondCost: number;
}

export interface RelationshipEventDefinition {
  type: string;
  trigger: string;
  description: string;
  options: RelationshipEventOption[];
}

export interface RelationshipEvent {
  type: string;
  trigger: string;
  description: string;
  options: RelationshipEventOption[];
  npcId: string;
  npcName: string;
}

export interface RelationshipEventRecord {
  playerId: string;
  npcId: string;
  eventType: string;
  resolutionChosen: string;
  affinityChange: number;
  diamondCost: number;
  occurredAt: Date;
}

export interface ProcessResolutionResult {
  success: boolean;
  message: string;
  affinityChange?: number;
  diamondCost?: number;
}

// Relationship event definitions
const RELATIONSHIP_EVENTS: RelationshipEventDefinition[] = [
  {
    type: 'argument',
    trigger: 'affinity_drop_15',
    description: 'You and {partner_name} had a disagreement. They seem upset.',
    options: [
      { choice: 'apologize', label: 'Apologize sincerely', affinityChange: 10, diamondCost: 0 },
      { choice: 'buy_gift', label: 'Buy a gift to make up', affinityChange: 25, diamondCost: 20 },
      { choice: 'ignore', label: 'Give them space', affinityChange: -10, diamondCost: 0 },
    ],
  },
  {
    type: 'anniversary',
    trigger: 'days_together_365',
    description: "It's your {years} year anniversary with {partner_name}!",
    options: [
      { choice: 'dinner_date', label: 'Take them to dinner', affinityChange: 5, diamondCost: 0 },
      { choice: 'luxury_date', label: 'Plan a luxury date', affinityChange: 15, diamondCost: 50 },
      { choice: 'forgot', label: 'You forgot...', affinityChange: -20, diamondCost: 0 },
    ],
  },
  {
    type: 'jealousy',
    trigger: 'talked_to_other_person',
    description: '{partner_name} seems jealous of your interactions with others.',
    options: [
      { choice: 'reassure', label: 'Reassure them', affinityChange: 5, diamondCost: 0 },
      { choice: 'dismiss', label: 'Dismiss their concerns', affinityChange: -15, diamondCost: 0 },
      { choice: 'grand_gesture', label: 'Make a grand gesture', affinityChange: 20, diamondCost: 30 },
    ],
  },
  {
    type: 'proposal',
    trigger: 'affinity_above_90',
    description: '{partner_name} wants to take your relationship to the next level.',
    options: [
      { choice: 'accept', label: 'Accept the proposal', affinityChange: 30, diamondCost: 0 },
      { choice: 'wait', label: 'Say you need more time', affinityChange: -5, diamondCost: 0 },
      { choice: 'decline', label: 'Decline politely', affinityChange: -40, diamondCost: 0 },
    ],
  },
  {
    type: 'breakup_threat',
    trigger: 'affinity_below_20',
    description: '{partner_name} is threatening to end the relationship.',
    options: [
      { choice: 'fix_it', label: 'Promise to do better', affinityChange: 15, diamondCost: 0 },
      { choice: 'therapy', label: 'Suggest couples therapy', affinityChange: 25, diamondCost: 40 },
      { choice: 'let_go', label: "Accept it's over", affinityChange: 0, diamondCost: 0 },
    ],
  },
  {
    type: 'gift_request',
    trigger: 'birthday_or_holiday',
    description: '{partner_name} mentioned wanting something special.',
    options: [
      { choice: 'thoughtful_gift', label: 'Give a thoughtful gift', affinityChange: 10, diamondCost: 0 },
      { choice: 'expensive_gift', label: 'Buy an expensive gift', affinityChange: 20, diamondCost: 25 },
      { choice: 'no_gift', label: 'No gift this time', affinityChange: -15, diamondCost: 0 },
    ],
  },
  {
    type: 'family_meeting',
    trigger: 'relationship_serious',
    description: '{partner_name} wants you to meet their family.',
    options: [
      { choice: 'excited', label: 'I would love to!', affinityChange: 15, diamondCost: 0 },
      { choice: 'nervous', label: "I'm a bit nervous...", affinityChange: 5, diamondCost: 0 },
      { choice: 'refuse', label: 'Not ready for that', affinityChange: -20, diamondCost: 0 },
    ],
  },
];

// In-memory event records (replace with database in production)
const eventRecords: RelationshipEventRecord[] = [];

// Track family meetings
const familyMeetings: Set<string> = new Set(); // "playerId:npcId"

/**
 * Get affinity change in last 24 hours for a relationship
 */
export function getAffinityChange24h(playerId: string, npcId: string): number {
  const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);

  return eventRecords
    .filter(
      record =>
        record.playerId === playerId &&
        record.npcId === npcId &&
        record.occurredAt >= yesterday
    )
    .reduce((sum, record) => sum + record.affinityChange, 0);
}

/**
 * Check if an event has occurred recently (within N days)
 */
export function hasRecentEvent(
  playerId: string,
  npcId: string,
  eventType: string,
  days = 7
): boolean {
  const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);

  return eventRecords.some(
    record =>
      record.playerId === playerId &&
      record.npcId === npcId &&
      record.eventType === eventType &&
      record.occurredAt >= cutoffDate
  );
}

/**
 * Check if player has already met NPC's family
 */
export function hasMetFamily(playerId: string, npcId: string): boolean {
  return familyMeetings.has(`${playerId}:${npcId}`);
}

/**
 * Get event definition by type and format with NPC data
 */
export function getEventByType(
  eventType: string,
  npc: Person,
  extraParams: Record<string, unknown> = {}
): RelationshipEvent | null {
  const eventDef = RELATIONSHIP_EVENTS.find(e => e.type === eventType);
  if (!eventDef) return null;

  const partnerName = `${npc.firstname} ${npc.lastname}`;
  let description = eventDef.description.replace('{partner_name}', partnerName);

  // Replace any extra params
  for (const [key, value] of Object.entries(extraParams)) {
    description = description.replace(`{${key}}`, String(value));
  }

  return {
    type: eventDef.type,
    trigger: eventDef.trigger,
    description,
    options: eventDef.options,
    npcId: npc.id,
    npcName: partnerName,
  };
}

/**
 * Check if any relationship events should trigger for a given NPC
 */
export function checkRelationshipTriggers(
  player: Player,
  npc: Person
): RelationshipEvent | null {
  // Skip if NPC is not in a romantic relationship
  const relationships = npc.relationships ?? [];
  const isRomantic = relationships.some(
    rel => ['partner', 'dating', 'girlfriend', 'boyfriend', 'spouse'].includes(rel.toLowerCase())
  );

  if (!isRomantic) {
    return null;
  }

  const playerId = player.userId;
  const affinity = npc.affinity ?? 0;

  // Check argument trigger (affinity drop)
  const affinityChange = getAffinityChange24h(playerId, npc.id);
  if (affinityChange <= -15) {
    if (!hasRecentEvent(playerId, npc.id, 'argument', 7)) {
      return getEventByType('argument', npc);
    }
  }

  // Check anniversary trigger
  // This would need integration with relationship start date tracking
  // For now, skip or use a random check

  // Check jealousy trigger (random with some relationship context)
  if (Math.random() < 0.01 && affinity < 70) {
    if (!hasRecentEvent(playerId, npc.id, 'jealousy', 14)) {
      return getEventByType('jealousy', npc);
    }
  }

  // Check proposal trigger (high affinity)
  if (affinity >= 90) {
    // Only if not married
    const isMarried = relationships.some(rel =>
      ['spouse', 'married', 'wife', 'husband'].includes(rel.toLowerCase())
    );

    if (!isMarried && !hasRecentEvent(playerId, npc.id, 'proposal', 180)) {
      return getEventByType('proposal', npc);
    }
  }

  // Check breakup threat trigger (low affinity)
  if (affinity <= 20 && affinity > -50) {
    if (!hasRecentEvent(playerId, npc.id, 'breakup_threat', 30)) {
      return getEventByType('breakup_threat', npc);
    }
  }

  // Check gift request trigger (random chance for birthdays/holidays)
  if (Math.random() < 0.005) {
    if (!hasRecentEvent(playerId, npc.id, 'gift_request', 30)) {
      return getEventByType('gift_request', npc);
    }
  }

  // Check family meeting trigger (serious relationships)
  if (affinity >= 70 && !hasMetFamily(playerId, npc.id)) {
    if (Math.random() < 0.02) {
      return getEventByType('family_meeting', npc);
    }
  }

  return null;
}

/**
 * Create a question event structure for the relationship event
 */
export function createRelationshipQuestionEvent(event: RelationshipEvent): {
  id: string;
  message: string;
  type: 'relationshipEvent';
  npcId: string;
  eventType: string;
  answers: Array<{
    label: string;
    choice: string;
    energyCost: number;
    diamondCost: number;
    affinityChange: number;
  }>;
} {
  return {
    id: `relationship_event_${event.type}_${event.npcId}`,
    message: event.description,
    type: 'relationshipEvent',
    npcId: event.npcId,
    eventType: event.type,
    answers: event.options.map(option => ({
      label: option.label,
      choice: option.choice,
      energyCost: 0,
      diamondCost: option.diamondCost,
      affinityChange: option.affinityChange,
    })),
  };
}

/**
 * Process the player's resolution choice for a relationship event
 */
export function processResolution(
  playerId: string,
  npc: Person,
  eventType: string,
  resolution: string,
  affinityChange: number,
  diamondCost: number,
  playerDiamonds: number
): ProcessResolutionResult {
  // Check if player has enough diamonds
  if (diamondCost > 0 && playerDiamonds < diamondCost) {
    return {
      success: false,
      message: `Not enough diamonds. Need ${diamondCost}, have ${playerDiamonds}`,
    };
  }

  // Update NPC affinity
  const currentAffinity = npc.affinity ?? 0;
  npc.affinity = Math.max(-100, Math.min(100, currentAffinity + affinityChange));

  // Record the event
  eventRecords.push({
    playerId,
    npcId: npc.id,
    eventType,
    resolutionChosen: resolution,
    affinityChange,
    diamondCost,
    occurredAt: new Date(),
  });

  // Track family meeting if applicable
  if (eventType === 'family_meeting' && ['excited', 'nervous'].includes(resolution)) {
    familyMeetings.add(`${playerId}:${npc.id}`);
  }

  // Generate result message
  const npcName = `${npc.firstname} ${npc.lastname}`;
  let affinityText = '';

  if (affinityChange > 0) {
    affinityText = ` Their affinity increased by ${affinityChange}!`;
  } else if (affinityChange < 0) {
    affinityText = ` Their affinity decreased by ${Math.abs(affinityChange)}.`;
  }

  return {
    success: true,
    message: `You resolved the situation with ${npcName}.${affinityText}`,
    affinityChange,
    diamondCost,
  };
}

/**
 * Get relationship event history for a player
 */
export function getEventHistory(
  playerId: string,
  npcId?: string,
  limit = 10
): RelationshipEventRecord[] {
  let filtered = eventRecords.filter(record => record.playerId === playerId);

  if (npcId) {
    filtered = filtered.filter(record => record.npcId === npcId);
  }

  return filtered
    .sort((a, b) => b.occurredAt.getTime() - a.occurredAt.getTime())
    .slice(0, limit);
}

/**
 * Get relationship statistics between player and NPC
 */
export function getRelationshipStats(
  playerId: string,
  npcId: string
): {
  totalEvents: number;
  totalAffinityChange: number;
  totalDiamondsSpent: number;
  firstEvent: Date | null;
  lastEvent: Date | null;
} {
  const records = eventRecords.filter(
    record => record.playerId === playerId && record.npcId === npcId
  );

  if (records.length === 0) {
    return {
      totalEvents: 0,
      totalAffinityChange: 0,
      totalDiamondsSpent: 0,
      firstEvent: null,
      lastEvent: null,
    };
  }

  const sorted = records.sort((a, b) => a.occurredAt.getTime() - b.occurredAt.getTime());

  return {
    totalEvents: records.length,
    totalAffinityChange: records.reduce((sum, r) => sum + r.affinityChange, 0),
    totalDiamondsSpent: records.reduce((sum, r) => sum + r.diamondCost, 0),
    firstEvent: sorted[0].occurredAt,
    lastEvent: sorted[sorted.length - 1].occurredAt,
  };
}

/**
 * Get all relationship event definitions
 */
export function getAllEventDefinitions(): RelationshipEventDefinition[] {
  return [...RELATIONSHIP_EVENTS];
}

/**
 * Clear event history (for testing)
 */
export function clearEventHistory(): void {
  eventRecords.length = 0;
  familyMeetings.clear();
}
