/**
 * NPC Interactions Module
 *
 * Handles location-based NPC meeting and interaction detection.
 * When the player and NPC are in the same location, interactions can trigger.
 *
 * Ported from Python ws/ functionality:
 * - Location-based NPC meeting/interaction triggers
 * - Interaction detection when player and NPC are in same location
 */

import { Player, Person } from '../../models/index.js';
import { EventResult, createMessageEvent, createQuestionEvent } from '../../events/base.js';
import { isNPCAvailable, getNPCCurrentActivity } from './npc_scheduler.js';

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

export type InteractionType =
  | 'greeting'
  | 'casual_chat'
  | 'deep_conversation'
  | 'activity_together'
  | 'request_help'
  | 'give_gift'
  | 'romantic';

export interface NPCInteraction {
  id: string;
  npcId: string;
  npcName: string;
  type: InteractionType;
  location: string;
  timestamp: number;
  message: string;
  affinityChange?: number;
  familiarityChange?: number;
}

export interface InteractionResult {
  interaction: NPCInteraction | null;
  event: EventResult | null;
  triggered: boolean;
}

export interface CoLocationInfo {
  npcId: string;
  npcName: string;
  location: string;
  locationType: string;
  npcActivity: string;
  playerActivity: string;
  relationship: string[];
  affinity: number;
  familiarity: number;
}

// ============================================================================
// Utility Functions
// ============================================================================

/**
 * Get random integer in range (inclusive)
 */
function randomInt(min: number, max: number): number {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Get random element from array
 */
function randomChoice<T>(arr: T[]): T | undefined {
  if (arr.length === 0) return undefined;
  return arr[Math.floor(Math.random() * arr.length)];
}

/**
 * Generate a unique interaction ID
 */
function generateInteractionId(): string {
  return `interaction_${Date.now()}_${randomInt(1000, 9999)}`;
}

/**
 * Get location type from location ID
 */
function getLocationType(locationId: string): string {
  if (locationId.startsWith('home-')) return 'home';
  if (locationId.startsWith('school-')) return 'school';
  if (locationId.startsWith('work-')) return 'work';
  if (locationId.startsWith('party-')) return 'party';
  if (locationId.startsWith('mall-')) return 'mall';
  if (locationId.startsWith('park-')) return 'park';
  if (locationId.startsWith('restaurant-')) return 'restaurant';
  if (locationId.startsWith('gym-')) return 'gym';
  return 'unknown';
}

/**
 * Get relationship title for an NPC
 */
function getRelationshipTitle(npc: Person): string {
  const relationships = npc.relationships ?? [];

  // Priority order for relationship display
  if (relationships.includes('partner')) return 'partner';
  if (relationships.includes('spouse')) return 'spouse';
  if (relationships.includes('mother')) return 'mother';
  if (relationships.includes('father')) return 'father';
  if (relationships.includes('sibling')) return 'sibling';
  if (relationships.includes('child')) return 'child';
  if (relationships.includes('grandparent')) return 'grandparent';
  if (relationships.includes('friend')) return 'friend';
  if (relationships.includes('classmate')) return 'classmate';
  if (relationships.includes('coworker')) return 'coworker';

  return 'acquaintance';
}

// ============================================================================
// Location-Based Detection
// ============================================================================

/**
 * Find all NPCs at the same location as the player
 * Returns list of NPCs sharing the player's current location
 */
export function findNPCsAtPlayerLocation(player: Player): CoLocationInfo[] {
  const coLocated: CoLocationInfo[] = [];
  const playerLocation = player.c?.location ?? '';

  if (!playerLocation) return coLocated;

  for (const npc of player.r ?? []) {
    if (npc.status !== 'alive') continue;

    // Check if NPC is at the same location
    if (npc.location === playerLocation) {
      const currentActivity = getNPCCurrentActivity(npc, player.hourOfDay ?? 0);

      coLocated.push({
        npcId: npc.id,
        npcName: `${npc.firstname} ${npc.lastname}`,
        location: playerLocation,
        locationType: getLocationType(playerLocation),
        npcActivity: currentActivity?.title ?? npc.intraDayMessage ?? '',
        playerActivity: player.c?.intraDayMessage ?? '',
        relationship: npc.relationships ?? [],
        affinity: npc.affinity ?? 0,
        familiarity: npc.familiarity ?? 0,
      });
    }
  }

  return coLocated;
}

/**
 * Find NPCs at a specific location
 */
export function findNPCsAtLocation(player: Player, locationId: string): Person[] {
  const npcsAtLocation: Person[] = [];

  for (const npc of player.r ?? []) {
    if (npc.status !== 'alive') continue;
    if (npc.location === locationId) {
      npcsAtLocation.push(npc);
    }
  }

  return npcsAtLocation;
}

/**
 * Check if player shares location with a specific NPC
 */
export function isPlayerWithNPC(player: Player, npcId: string): boolean {
  const playerLocation = player.c?.location ?? '';
  const npc = player.r?.find(n => n.id === npcId);

  if (!npc) return false;

  return npc.location === playerLocation;
}

// ============================================================================
// Interaction Generation
// ============================================================================

/**
 * Determine interaction type based on relationship and context
 */
function determineInteractionType(
  npc: Person,
  player: Player,
  locationType: string
): InteractionType {
  const relationships = npc.relationships ?? [];
  const affinity = npc.affinity ?? 0;
  const familiarity = npc.familiarity ?? 0;

  // Romantic interactions for high-affinity partners
  if (
    (relationships.includes('partner') || relationships.includes('spouse')) &&
    affinity >= 60
  ) {
    if (randomInt(0, 100) < 30) {
      return 'romantic';
    }
  }

  // Activity together at certain locations
  if (['gym', 'park', 'mall'].includes(locationType) && familiarity > 50) {
    if (randomInt(0, 100) < 40) {
      return 'activity_together';
    }
  }

  // Deep conversations with close friends/family
  if (
    (relationships.includes('friend') ||
      relationships.includes('mother') ||
      relationships.includes('father') ||
      relationships.includes('sibling')) &&
    affinity >= 50
  ) {
    if (randomInt(0, 100) < 25) {
      return 'deep_conversation';
    }
  }

  // High familiarity leads to casual chats
  if (familiarity >= 30) {
    if (randomInt(0, 100) < 50) {
      return 'casual_chat';
    }
  }

  // Default to greeting
  return 'greeting';
}

/**
 * Generate greeting messages based on relationship
 */
function generateGreetingMessage(
  npc: Person,
  player: Player,
  locationType: string
): string {
  const relationship = getRelationshipTitle(npc);
  const firstName = npc.firstname;
  const playerName = player.c?.firstname ?? 'you';

  const greetings: Record<string, string[]> = {
    home: [
      `${firstName} waves at ${playerName} from across the room.`,
      `You see your ${relationship} ${firstName} at home.`,
      `${firstName} nods in greeting as you pass by.`,
    ],
    school: [
      `${firstName} waves at you in the hallway.`,
      `You spot ${firstName} between classes.`,
      `${firstName} says "Hey!" as you pass their locker.`,
    ],
    work: [
      `${firstName} greets you at the office.`,
      `You see ${firstName} by the water cooler.`,
      `${firstName} waves from their desk.`,
    ],
    party: [
      `${firstName} spots you across the room and waves.`,
      `You bump into ${firstName} at the party.`,
      `${firstName} comes over to say hi.`,
    ],
    default: [
      `You run into ${firstName}.`,
      `${firstName} notices you and waves.`,
      `You spot your ${relationship} ${firstName} nearby.`,
    ],
  };

  const locationGreetings = greetings[locationType] ?? greetings['default'];
  return randomChoice(locationGreetings) ?? `You see ${firstName}.`;
}

/**
 * Generate interaction message based on type
 */
function generateInteractionMessage(
  type: InteractionType,
  npc: Person,
  player: Player,
  locationType: string
): string {
  const firstName = npc.firstname;
  const relationship = getRelationshipTitle(npc);

  switch (type) {
    case 'greeting':
      return generateGreetingMessage(npc, player, locationType);

    case 'casual_chat':
      const casualTopics = [
        `${firstName} tells you about their day.`,
        `You and ${firstName} chat about the weather.`,
        `${firstName} shares some local gossip with you.`,
        `You catch up with ${firstName} briefly.`,
        `${firstName} asks how you've been lately.`,
      ];
      return randomChoice(casualTopics) ?? `You chat with ${firstName}.`;

    case 'deep_conversation':
      const deepTopics = [
        `${firstName} opens up about something personal.`,
        `You have a meaningful conversation with ${firstName}.`,
        `${firstName} shares their hopes and dreams with you.`,
        `You and ${firstName} discuss life's big questions.`,
        `${firstName} confides in you about their worries.`,
      ];
      return randomChoice(deepTopics) ?? `You have a deep talk with ${firstName}.`;

    case 'activity_together':
      const activities: Record<string, string[]> = {
        gym: [
          `You work out together with ${firstName}.`,
          `${firstName} spots you during your exercise.`,
        ],
        park: [
          `You take a walk with ${firstName}.`,
          `${firstName} joins you for a jog.`,
        ],
        mall: [
          `You go shopping with ${firstName}.`,
          `${firstName} helps you pick out some items.`,
        ],
        default: [
          `You spend time with ${firstName}.`,
          `${firstName} joins you for a while.`,
        ],
      };
      const activityList = activities[locationType] ?? activities['default'];
      return randomChoice(activityList) ?? `You spend time with ${firstName}.`;

    case 'romantic':
      const romanticMessages = [
        `${firstName} gives you a warm embrace.`,
        `You share a tender moment with ${firstName}.`,
        `${firstName} holds your hand affectionately.`,
        `You and ${firstName} share a loving glance.`,
      ];
      return randomChoice(romanticMessages) ?? `You share a moment with ${firstName}.`;

    case 'request_help':
      const helpRequests = [
        `${firstName} asks for your advice on something.`,
        `${firstName} needs your help with a problem.`,
        `${firstName} seeks your opinion about an issue.`,
      ];
      return randomChoice(helpRequests) ?? `${firstName} asks for your help.`;

    case 'give_gift':
      return `${firstName} gives you a small gift.`;

    default:
      return `You interact with ${firstName}.`;
  }
}

/**
 * Calculate affinity and familiarity changes from interaction
 */
function calculateInteractionEffects(
  type: InteractionType,
  npc: Person
): { affinityChange: number; familiarityChange: number } {
  let affinityChange = 0;
  let familiarityChange = 0;

  switch (type) {
    case 'greeting':
      familiarityChange = randomInt(0, 1);
      break;

    case 'casual_chat':
      affinityChange = randomInt(0, 2);
      familiarityChange = randomInt(1, 3);
      break;

    case 'deep_conversation':
      affinityChange = randomInt(2, 5);
      familiarityChange = randomInt(3, 5);
      break;

    case 'activity_together':
      affinityChange = randomInt(1, 4);
      familiarityChange = randomInt(2, 4);
      break;

    case 'romantic':
      affinityChange = randomInt(3, 7);
      familiarityChange = randomInt(2, 5);
      break;

    case 'request_help':
      affinityChange = randomInt(1, 3);
      familiarityChange = randomInt(1, 2);
      break;

    case 'give_gift':
      affinityChange = randomInt(5, 10);
      familiarityChange = randomInt(1, 3);
      break;
  }

  return { affinityChange, familiarityChange };
}

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

/**
 * Check for and potentially trigger an NPC interaction
 * Called during game loop when player is at a location with NPCs
 *
 * @param player - The player object
 * @param interactionChance - Base chance (0-100) for interaction to occur
 * @returns Interaction result with potential event
 */
export function checkNPCInteraction(
  player: Player,
  interactionChance: number = 15
): InteractionResult {
  // Find NPCs at player's location
  const coLocatedNPCs = findNPCsAtPlayerLocation(player);

  if (coLocatedNPCs.length === 0) {
    return { interaction: null, event: null, triggered: false };
  }

  // Check if interaction occurs (random chance)
  if (randomInt(0, 100) > interactionChance) {
    return { interaction: null, event: null, triggered: false };
  }

  // Select a random NPC to interact with
  const selectedInfo = randomChoice(coLocatedNPCs);
  if (!selectedInfo) {
    return { interaction: null, event: null, triggered: false };
  }

  // Find the full NPC object
  const npc = player.r?.find(n => n.id === selectedInfo.npcId);
  if (!npc) {
    return { interaction: null, event: null, triggered: false };
  }

  // Check if NPC is available for interaction
  if (!isNPCAvailable(npc, player.hourOfDay ?? 0)) {
    return { interaction: null, event: null, triggered: false };
  }

  // Determine interaction type
  const interactionType = determineInteractionType(
    npc,
    player,
    selectedInfo.locationType
  );

  // Generate interaction message
  const message = generateInteractionMessage(
    interactionType,
    npc,
    player,
    selectedInfo.locationType
  );

  // Calculate effects
  const effects = calculateInteractionEffects(interactionType, npc);

  // Create interaction record
  const interaction: NPCInteraction = {
    id: generateInteractionId(),
    npcId: npc.id,
    npcName: `${npc.firstname} ${npc.lastname}`,
    type: interactionType,
    location: selectedInfo.location,
    timestamp: Date.now(),
    message,
    affinityChange: effects.affinityChange,
    familiarityChange: effects.familiarityChange,
  };

  // Apply effects to NPC
  if (effects.affinityChange > 0) {
    npc.affinity = Math.min(100, (npc.affinity ?? 0) + effects.affinityChange);
  }
  if (effects.familiarityChange > 0) {
    npc.familiarity = Math.min(100, (npc.familiarity ?? 0) + effects.familiarityChange);
  }

  // Create event for the player
  const event = createMessageEvent(
    `npc_interaction_${interaction.id}`,
    message,
    player,
    true,
    {
      affinityChange: effects.affinityChange,
    }
  );

  return {
    interaction,
    event,
    triggered: true,
  };
}

/**
 * Trigger a specific interaction with an NPC
 * Used when player initiates conversation or action
 */
export function triggerNPCInteraction(
  player: Player,
  npcId: string,
  type: InteractionType
): InteractionResult {
  const npc = player.r?.find(n => n.id === npcId);

  if (!npc || npc.status !== 'alive') {
    return { interaction: null, event: null, triggered: false };
  }

  // Check if player and NPC are at same location
  if (!isPlayerWithNPC(player, npcId)) {
    return { interaction: null, event: null, triggered: false };
  }

  // Check if NPC is available
  if (!isNPCAvailable(npc, player.hourOfDay ?? 0)) {
    const busyMessage = `${npc.firstname} is busy right now.`;
    return {
      interaction: null,
      event: createMessageEvent(
        `npc_busy_${npcId}`,
        busyMessage,
        player,
        false
      ),
      triggered: false,
    };
  }

  const locationType = getLocationType(npc.location ?? '');
  const message = generateInteractionMessage(type, npc, player, locationType);
  const effects = calculateInteractionEffects(type, npc);

  const interaction: NPCInteraction = {
    id: generateInteractionId(),
    npcId: npc.id,
    npcName: `${npc.firstname} ${npc.lastname}`,
    type,
    location: npc.location ?? '',
    timestamp: Date.now(),
    message,
    affinityChange: effects.affinityChange,
    familiarityChange: effects.familiarityChange,
  };

  // Apply effects
  if (effects.affinityChange > 0) {
    npc.affinity = Math.min(100, (npc.affinity ?? 0) + effects.affinityChange);
  }
  if (effects.familiarityChange > 0) {
    npc.familiarity = Math.min(100, (npc.familiarity ?? 0) + effects.familiarityChange);
  }

  const event = createMessageEvent(
    `npc_interaction_${interaction.id}`,
    message,
    player,
    true,
    {
      affinityChange: effects.affinityChange,
    }
  );

  return {
    interaction,
    event,
    triggered: true,
  };
}

/**
 * Get all possible interactions at current location
 * Returns list of NPCs player can interact with
 */
export function getAvailableInteractions(player: Player): Array<{
  npcId: string;
  npcName: string;
  relationship: string;
  availableTypes: InteractionType[];
}> {
  const available: Array<{
    npcId: string;
    npcName: string;
    relationship: string;
    availableTypes: InteractionType[];
  }> = [];

  const coLocatedNPCs = findNPCsAtPlayerLocation(player);

  for (const info of coLocatedNPCs) {
    const npc = player.r?.find(n => n.id === info.npcId);
    if (!npc) continue;

    if (!isNPCAvailable(npc, player.hourOfDay ?? 0)) continue;

    const types: InteractionType[] = ['greeting', 'casual_chat'];

    // Add more options based on relationship
    if ((npc.affinity ?? 0) >= 50) {
      types.push('deep_conversation');
    }

    if (
      npc.relationships?.includes('partner') ||
      npc.relationships?.includes('spouse')
    ) {
      types.push('romantic');
    }

    if (['gym', 'park', 'mall'].includes(info.locationType)) {
      types.push('activity_together');
    }

    available.push({
      npcId: npc.id,
      npcName: `${npc.firstname} ${npc.lastname}`,
      relationship: getRelationshipTitle(npc),
      availableTypes: types,
    });
  }

  return available;
}

/**
 * Process hourly NPC interactions
 * Called once per hour to check for random encounters
 */
export function processHourlyNPCInteractions(player: Player): InteractionResult[] {
  const results: InteractionResult[] = [];

  // Base interaction chance - higher at certain times/locations
  let interactionChance = 10;

  const hour = player.hourOfDay ?? 0;
  const locationType = getLocationType(player.c?.location ?? '');

  // Higher chance during social hours (evening)
  if (hour >= 18 && hour <= 22) {
    interactionChance += 10;
  }

  // Higher chance at social locations
  if (['party', 'mall', 'park', 'restaurant'].includes(locationType)) {
    interactionChance += 15;
  }

  // Lower chance at work/school (focused activities)
  if (['work', 'school'].includes(locationType)) {
    interactionChance -= 5;
  }

  // Check for interaction
  const result = checkNPCInteraction(player, interactionChance);
  if (result.triggered) {
    results.push(result);
  }

  return results;
}

// ============================================================================
// Exports
// ============================================================================

export const npcInteractions = {
  findNPCsAtPlayerLocation,
  findNPCsAtLocation,
  isPlayerWithNPC,
  checkNPCInteraction,
  triggerNPCInteraction,
  getAvailableInteractions,
  processHourlyNPCInteractions,
};
