/**
 * NPC Scheduler Module
 *
 * Generates and manages daily schedules for NPC relationship characters.
 * NPCs follow their own daily routines based on occupation, age, and schedules.
 *
 * Ported from Python ws/intradayActivity.py - specifically the NPC aspects:
 * - NPC daily plan generation for relationship characters
 * - NPC movement patterns based on their schedules
 * - handleNPCSchedules() - Process NPC schedules
 */

import { Player, Person, GameLocation } from '../../models/index.js';
import { DailyEvent, Schedule } from '../../game/engine/intradayActivity.js';

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

export interface NPCScheduleEvent {
  time: number;
  location: string;
  title: string;
  name: string;
  npcId: string;
}

export interface NPCLocationUpdate {
  npcId: string;
  previousLocation: string;
  newLocation: string;
  time: number;
  activity: string;
}

// ============================================================================
// 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)];
}

/**
 * Find a location by ID
 */
function findLocationById(locations: GameLocation[], id: string): GameLocation | undefined {
  return locations.find(l => l.id === id);
}

/**
 * Create a new location
 */
function createLocation(id: string, type: string): GameLocation {
  return { id, type };
}

/**
 * Create a daily event for an NPC
 */
function createDailyEvent(time: number, location: string): DailyEvent {
  return {
    time,
    location,
    title: '',
    name: '',
  };
}

/**
 * Get day name from day of week number
 * dayOfWeek: 1=Sunday, 2=Monday, ..., 6=Friday, 7=Saturday
 */
function getDayName(dayOfWeek: number): string {
  const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
  return days[(dayOfWeek - 1) % 7] || 'Monday';
}

/**
 * Check if it's a weekend
 */
function isWeekendDay(dayOfWeek: number): boolean {
  return dayOfWeek === 1 || dayOfWeek === 7;
}

// ============================================================================
// NPC Daily Plan Generation
// ============================================================================

/**
 * Generate NPC-specific daily plan
 * NPCs share some locations with the player (school, work) but have their own homes
 */
export function generateNPCDailyPlan(player: Player, npc: Person): Person {
  npc.dailyPlan = [];
  const locations = (player.l ?? []) as GameLocation[];

  // NPCs who are family (familyLevel == 1) share player's home
  // Others have their own homes
  let homeId = `home-${npc.id}`;
  if (npc.familyLevel === 1 || npc.relationships?.includes('self')) {
    homeId = `home-${player.c?.id ?? npc.id}`;
  }

  // Get or create home location
  let homeObj = findLocationById(locations, homeId);
  if (!homeObj) {
    homeObj = createLocation(homeId, 'home');
    locations.push(homeObj);
  }
  const home = homeObj.id;

  // NPCs may share school with player if they are classmates
  const isClassmate = npc.relationships?.includes('classmate');
  const schoolId = isClassmate
    ? `school-${player.c?.id ?? npc.id}`
    : `school-${npc.id}`;

  let schoolObj = findLocationById(locations, schoolId);
  if (!schoolObj) {
    schoolObj = createLocation(schoolId, 'school');
    locations.push(schoolObj);
  }
  const school = schoolObj.id;

  // NPCs may share workplace with player if they are coworkers
  const isCoworker = npc.relationships?.includes('coworker');
  const workId = isCoworker
    ? `work-${player.c?.id ?? npc.id}`
    : `work-${npc.id}`;

  let workObj = findLocationById(locations, workId);
  if (!workObj) {
    workObj = createLocation(workId, 'work');
    locations.push(workObj);
  }
  const work = workObj.id;

  // Update locations array on player
  player.l = locations;

  const isWeekend = isWeekendDay(player.dayOfWeek ?? 1);
  const dayName = getDayName(player.dayOfWeek ?? 1);

  // Process custom schedules (like activities, appointments)
  const schedules = (npc.schedules ?? []) as Schedule[];
  for (const schedule of schedules) {
    if (
      schedule.duration &&
      schedule.days &&
      (schedule.executions ?? 0) < schedule.duration &&
      schedule.days.daysOfWeek?.includes(dayName)
    ) {
      const e = createDailyEvent(schedule.days.hour ?? 12, schedule.location ?? home);
      e.title = schedule.title ?? 'Scheduled activity';
      e.name = schedule.id ?? 'schedule';
      schedule.executions = (schedule.executions ?? 0) + 1;
      npc.dailyPlan.push(e);
    }
  }

  // Generate occupation-based daily plan
  const age = npc.ageYears ?? 0;
  const occupation = npc.occupation ?? '';

  // Retired NPCs
  if (age > 50 && occupation === 'retired') {
    generateRetiredNPCPlan(npc, home);
    return npc;
  }

  // Working adult NPCs
  if (age > 18 && occupation !== 'student' && occupation !== 'preschool' && occupation !== 'retired') {
    generateWorkerNPCPlan(npc, home, work, isWeekend);
    return npc;
  }

  // Preschool NPCs (young children)
  if (occupation === 'preschool' || age < 5) {
    generatePreschoolNPCPlan(npc, home);
    return npc;
  }

  // Student NPCs
  if (occupation === 'student') {
    if (isWeekend || player.summerVacation) {
      generateStudentWeekendNPCPlan(player, npc, home);
    } else {
      generateStudentSchoolDayNPCPlan(npc, home, school);
    }
    return npc;
  }

  // Default: stay at home schedule for unknown occupations
  generateDefaultNPCPlan(npc, home);
  return npc;
}

/**
 * Generate retired NPC daily plan
 */
function generateRetiredNPCPlan(npc: Person, home: string): void {
  const wakeUpTime = randomInt(7, 9);

  const wakeUp = createDailyEvent(wakeUpTime, home);
  wakeUp.title = `${npc.firstname} wakes up`;
  wakeUp.name = 'wakeUp';
  npc.dailyPlan!.push(wakeUp);

  const breakfast = createDailyEvent(wakeUpTime + 1, home);
  breakfast.title = `${npc.firstname} eats breakfast`;
  breakfast.name = 'breakfast';
  npc.dailyPlan!.push(breakfast);

  // Morning activity - random choice
  const morningActivity = createDailyEvent(10, home);
  const morningActivities = [
    'reads the newspaper',
    'tends to the garden',
    'takes a morning walk',
    'watches TV',
  ];
  morningActivity.title = `${npc.firstname} ${randomChoice(morningActivities)}`;
  morningActivity.name = 'leisure';
  npc.dailyPlan!.push(morningActivity);

  const lunch = createDailyEvent(12, home);
  lunch.title = `${npc.firstname} eats lunch`;
  lunch.name = 'lunch';
  npc.dailyPlan!.push(lunch);

  // Afternoon activity
  const afternoonActivity = createDailyEvent(14, home);
  const afternoonActivities = [
    'takes a nap',
    'visits a neighbor',
    'goes for a walk',
    'reads a book',
  ];
  afternoonActivity.title = `${npc.firstname} ${randomChoice(afternoonActivities)}`;
  afternoonActivity.name = 'leisure';
  npc.dailyPlan!.push(afternoonActivity);

  const dinner = createDailyEvent(18, home);
  dinner.title = `${npc.firstname} eats dinner`;
  dinner.name = 'dinner';
  npc.dailyPlan!.push(dinner);

  const bed = createDailyEvent(21, home);
  bed.title = `${npc.firstname} goes to bed`;
  bed.name = 'bed';
  npc.dailyPlan!.push(bed);
}

/**
 * Generate working adult NPC daily plan
 */
function generateWorkerNPCPlan(
  npc: Person,
  home: string,
  work: string,
  isWeekend: boolean
): void {
  const wakeUpTime = randomInt(5, 8);

  const wakeUp = createDailyEvent(wakeUpTime, home);
  wakeUp.title = `${npc.firstname} wakes up`;
  wakeUp.name = 'wakeUp';
  npc.dailyPlan!.push(wakeUp);

  if (!isWeekend) {
    // Weekday - go to work
    const toWork = createDailyEvent(wakeUpTime + 1, home);
    toWork.title = `${npc.firstname} is on the way to work`;
    toWork.name = 'commute';
    npc.dailyPlan!.push(toWork);

    const arriveWork = createDailyEvent(wakeUpTime + 2, work);
    arriveWork.title = `${npc.firstname} arrives at work`;
    arriveWork.name = 'work';
    npc.dailyPlan!.push(arriveWork);

    const lunch = createDailyEvent(12, work);
    lunch.title = `${npc.firstname} takes lunch break`;
    lunch.name = 'lunch';
    npc.dailyPlan!.push(lunch);

    const finishWork = createDailyEvent(17, work);
    finishWork.title = `${npc.firstname} finishes work`;
    finishWork.name = 'work';
    npc.dailyPlan!.push(finishWork);

    const arriveHome = createDailyEvent(18, home);
    arriveHome.title = `${npc.firstname} arrives home`;
    arriveHome.name = 'home';
    npc.dailyPlan!.push(arriveHome);

    const dinner = createDailyEvent(19, home);
    dinner.title = `${npc.firstname} eats dinner`;
    dinner.name = 'dinner';
    npc.dailyPlan!.push(dinner);

    const relax = createDailyEvent(20, home);
    relax.title = `${npc.firstname} relaxes at home`;
    relax.name = 'relax';
    npc.dailyPlan!.push(relax);

    // Bed time
    let bedTime = wakeUpTime + 16; // 8 hours of sleep before next day
    if (bedTime >= 24) bedTime -= 24;
    const bed = createDailyEvent(bedTime, home);
    bed.title = `${npc.firstname} goes to bed`;
    bed.name = 'bed';
    npc.dailyPlan!.push(bed);
  } else {
    // Weekend
    const breakfast = createDailyEvent(wakeUpTime + 1, home);
    breakfast.title = `${npc.firstname} eats breakfast`;
    breakfast.name = 'breakfast';
    npc.dailyPlan!.push(breakfast);

    // Weekend activity
    const morningActivity = createDailyEvent(10, home);
    const weekendActivities = [
      'does errands',
      'relaxes at home',
      'goes out shopping',
      'visits friends',
      'exercises',
    ];
    morningActivity.title = `${npc.firstname} ${randomChoice(weekendActivities)}`;
    morningActivity.name = 'leisure';
    npc.dailyPlan!.push(morningActivity);

    const lunch = createDailyEvent(12, home);
    lunch.title = `${npc.firstname} eats lunch`;
    lunch.name = 'lunch';
    npc.dailyPlan!.push(lunch);

    const afternoonActivity = createDailyEvent(14, home);
    afternoonActivity.title = `${npc.firstname} ${randomChoice(weekendActivities)}`;
    afternoonActivity.name = 'leisure';
    npc.dailyPlan!.push(afternoonActivity);

    const dinner = createDailyEvent(18, home);
    dinner.title = `${npc.firstname} eats dinner`;
    dinner.name = 'dinner';
    npc.dailyPlan!.push(dinner);

    const bed = createDailyEvent(23, home);
    bed.title = `${npc.firstname} goes to bed`;
    bed.name = 'bed';
    npc.dailyPlan!.push(bed);
  }
}

/**
 * Generate preschool/infant NPC daily plan
 */
function generatePreschoolNPCPlan(npc: Person, home: string): void {
  const wakeUpTime = randomInt(7, 10);

  const wakeUp = createDailyEvent(wakeUpTime, home);
  wakeUp.title = `${npc.firstname} wakes up`;
  wakeUp.name = 'wakeUp';
  npc.dailyPlan!.push(wakeUp);

  const breakfast = createDailyEvent(wakeUpTime + 1, home);
  breakfast.title = `${npc.firstname} is fed breakfast`;
  breakfast.name = 'breakfast';
  npc.dailyPlan!.push(breakfast);

  const play = createDailyEvent(10, home);
  play.title = `${npc.firstname} plays`;
  play.name = 'play';
  npc.dailyPlan!.push(play);

  const lunch = createDailyEvent(12, home);
  lunch.title = `${npc.firstname} is fed lunch`;
  lunch.name = 'lunch';
  npc.dailyPlan!.push(lunch);

  const nap = createDailyEvent(13, home);
  nap.title = `${npc.firstname} takes a nap`;
  nap.name = 'nap';
  npc.dailyPlan!.push(nap);

  const afternoonPlay = createDailyEvent(15, home);
  afternoonPlay.title = `${npc.firstname} plays`;
  afternoonPlay.name = 'play';
  npc.dailyPlan!.push(afternoonPlay);

  const dinner = createDailyEvent(18, home);
  dinner.title = `${npc.firstname} is fed dinner`;
  dinner.name = 'dinner';
  npc.dailyPlan!.push(dinner);

  const bed = createDailyEvent(20, home);
  bed.title = `${npc.firstname} goes to bed`;
  bed.name = 'bed';
  npc.dailyPlan!.push(bed);
}

/**
 * Generate student NPC weekend/summer plan
 */
function generateStudentWeekendNPCPlan(player: Player, npc: Person, home: string): void {
  const wakeUpTime = randomInt(8, 11);

  const wakeUp = createDailyEvent(wakeUpTime, home);
  wakeUp.title = `${npc.firstname} wakes up`;
  wakeUp.name = 'wakeUp';
  npc.dailyPlan!.push(wakeUp);

  const breakfast = createDailyEvent(wakeUpTime + 1, home);
  breakfast.title = `${npc.firstname} eats breakfast`;
  breakfast.name = 'breakfast';
  npc.dailyPlan!.push(breakfast);

  // Random weekend activity
  const morningActivity = createDailyEvent(wakeUpTime + 2, home);
  const activities = [
    'plays video games',
    'hangs out with friends',
    'goes to the mall',
    'watches TV',
    'does homework',
    'practices a hobby',
  ];
  morningActivity.title = `${npc.firstname} ${randomChoice(activities)}`;
  morningActivity.name = 'leisure';
  npc.dailyPlan!.push(morningActivity);

  const lunch = createDailyEvent(13, home);
  lunch.title = `${npc.firstname} eats lunch`;
  lunch.name = 'lunch';
  npc.dailyPlan!.push(lunch);

  const afternoonActivity = createDailyEvent(15, home);
  afternoonActivity.title = `${npc.firstname} ${randomChoice(activities)}`;
  afternoonActivity.name = 'leisure';
  npc.dailyPlan!.push(afternoonActivity);

  const dinner = createDailyEvent(18, home);
  dinner.title = `${npc.firstname} eats dinner`;
  dinner.name = 'dinner';
  npc.dailyPlan!.push(dinner);

  // Evening activity
  const eveningActivity = createDailyEvent(20, home);
  if (randomInt(0, 3) === 0) {
    eveningActivity.title = `${npc.firstname} does homework`;
    eveningActivity.name = 'study';
  } else {
    eveningActivity.title = `${npc.firstname} relaxes at home`;
    eveningActivity.name = 'relax';
  }
  npc.dailyPlan!.push(eveningActivity);

  const bed = createDailyEvent(randomInt(22, 24), home);
  bed.title = `${npc.firstname} goes to sleep`;
  bed.name = 'sleep';
  npc.dailyPlan!.push(bed);
}

/**
 * Generate student NPC school day plan
 */
function generateStudentSchoolDayNPCPlan(npc: Person, home: string, school: string): void {
  const wakeUp = createDailyEvent(7, home);
  wakeUp.title = `${npc.firstname} wakes up for school`;
  wakeUp.name = 'wakeUp';
  npc.dailyPlan!.push(wakeUp);

  const toSchool = createDailyEvent(8, home);
  toSchool.title = `${npc.firstname} is on the way to school`;
  toSchool.name = 'commute';
  npc.dailyPlan!.push(toSchool);

  const arriveSchool = createDailyEvent(9, school);
  arriveSchool.title = `${npc.firstname} arrives at school`;
  arriveSchool.name = 'school';
  npc.dailyPlan!.push(arriveSchool);

  const lunch = createDailyEvent(12, school);
  lunch.title = `${npc.firstname} eats lunch at school`;
  lunch.name = 'lunch';
  npc.dailyPlan!.push(lunch);

  // Afternoon classes
  const afternoonClass = createDailyEvent(13, school);
  afternoonClass.title = `${npc.firstname} is in class`;
  afternoonClass.name = 'school';
  npc.dailyPlan!.push(afternoonClass);

  // After school - may have extracurriculars
  const activities = npc.activities ?? [];
  const extracurriculars = activities.filter((a: unknown) => {
    if (typeof a === 'object' && a !== null && 'type' in a) {
      return (a as { type: string }).type === 'extracurricular';
    }
    return false;
  });

  let currentTime = 15;
  if (extracurriculars.length > 0) {
    const activity = randomChoice(extracurriculars) as { title?: string } | undefined;
    if (activity?.title) {
      const practice = createDailyEvent(currentTime, school);
      practice.title = `${npc.firstname} goes to ${activity.title} practice`;
      practice.name = 'extracurricular';
      npc.dailyPlan!.push(practice);
      currentTime = 16;
    }
  }

  const goingHome = createDailyEvent(currentTime, school);
  goingHome.title = `${npc.firstname} leaves school`;
  goingHome.name = 'commute';
  npc.dailyPlan!.push(goingHome);

  const arriveHome = createDailyEvent(currentTime + 1, home);
  arriveHome.title = `${npc.firstname} arrives home`;
  arriveHome.name = 'home';
  npc.dailyPlan!.push(arriveHome);

  const dinner = createDailyEvent(18, home);
  dinner.title = `${npc.firstname} eats dinner`;
  dinner.name = 'dinner';
  npc.dailyPlan!.push(dinner);

  // Homework or relaxation
  const evening = createDailyEvent(19, home);
  if (randomInt(0, 1) === 0) {
    evening.title = `${npc.firstname} does homework`;
    evening.name = 'study';
  } else {
    evening.title = `${npc.firstname} relaxes at home`;
    evening.name = 'relax';
  }
  npc.dailyPlan!.push(evening);

  const bed = createDailyEvent(randomInt(21, 23), home);
  bed.title = `${npc.firstname} goes to sleep`;
  bed.name = 'sleep';
  npc.dailyPlan!.push(bed);
}

/**
 * Generate default NPC plan for unknown occupations
 */
function generateDefaultNPCPlan(npc: Person, home: string): void {
  const wakeUpTime = randomInt(7, 10);

  const wakeUp = createDailyEvent(wakeUpTime, home);
  wakeUp.title = `${npc.firstname} wakes up`;
  wakeUp.name = 'wakeUp';
  npc.dailyPlan!.push(wakeUp);

  const breakfast = createDailyEvent(wakeUpTime + 1, home);
  breakfast.title = `${npc.firstname} eats breakfast`;
  breakfast.name = 'breakfast';
  npc.dailyPlan!.push(breakfast);

  const morning = createDailyEvent(10, home);
  morning.title = `${npc.firstname} goes about their day`;
  morning.name = 'activity';
  npc.dailyPlan!.push(morning);

  const lunch = createDailyEvent(12, home);
  lunch.title = `${npc.firstname} eats lunch`;
  lunch.name = 'lunch';
  npc.dailyPlan!.push(lunch);

  const afternoon = createDailyEvent(14, home);
  afternoon.title = `${npc.firstname} continues their day`;
  afternoon.name = 'activity';
  npc.dailyPlan!.push(afternoon);

  const dinner = createDailyEvent(18, home);
  dinner.title = `${npc.firstname} eats dinner`;
  dinner.name = 'dinner';
  npc.dailyPlan!.push(dinner);

  const evening = createDailyEvent(20, home);
  evening.title = `${npc.firstname} relaxes`;
  evening.name = 'relax';
  npc.dailyPlan!.push(evening);

  const bed = createDailyEvent(22, home);
  bed.title = `${npc.firstname} goes to bed`;
  bed.name = 'bed';
  npc.dailyPlan!.push(bed);
}

// ============================================================================
// NPC Schedule Processing
// ============================================================================

/**
 * Process NPC schedules for all relationship characters
 * This is called at the start of each day to generate daily plans
 *
 * Matches Python loop_manager.py behavior:
 * - Generates daily plans for all NPCs in player.r
 * - Only processes alive NPCs
 */
export function handleNPCSchedules(player: Player): NPCLocationUpdate[] {
  const updates: NPCLocationUpdate[] = [];

  for (let i = 0; i < (player.r?.length ?? 0); i++) {
    const npc = player.r[i];

    // Skip dead NPCs
    if (npc.status !== 'alive') continue;

    // Store previous location
    const previousLocation = npc.location ?? '';

    // Generate new daily plan
    player.r[i] = generateNPCDailyPlan(player, npc);

    // Track location change if any
    if (npc.location !== previousLocation) {
      updates.push({
        npcId: npc.id,
        previousLocation,
        newLocation: npc.location,
        time: player.hourOfDay ?? 0,
        activity: npc.intraDayMessage ?? '',
      });
    }
  }

  return updates;
}

/**
 * Update NPC locations based on current time
 * This is called each hour to update NPC positions
 *
 * Matches Python getIntradayActivity() behavior for NPCs
 */
export function updateNPCLocations(player: Player): NPCLocationUpdate[] {
  const updates: NPCLocationUpdate[] = [];
  const currentTime = player.hourOfDay ?? 0;

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

    const plan = npc.dailyPlan ?? [];
    const previousLocation = npc.location ?? '';

    // Find event matching current time
    for (const event of plan) {
      if (typeof event === 'object' && event !== null && 'time' in event) {
        const e = event as DailyEvent;
        if (e.time === currentTime) {
          // Update location
          if (e.location !== npc.location) {
            npc.location = e.location;

            updates.push({
              npcId: npc.id,
              previousLocation,
              newLocation: npc.location,
              time: currentTime,
              activity: e.title ?? '',
            });
          }

          // Update intraday message
          npc.lastIntraDayMessage = npc.intraDayMessage;
          if (e.title) {
            npc.intraDayMessage = e.title;
          }
          break;
        }
      }
    }
  }

  return updates;
}

/**
 * Get NPC's current activity based on their daily plan
 */
export function getNPCCurrentActivity(npc: Person, currentHour: number): DailyEvent | null {
  const plan = npc.dailyPlan ?? [];

  // Find the most recent activity at or before current time
  let currentActivity: DailyEvent | null = null;

  for (const event of plan) {
    if (typeof event === 'object' && event !== null && 'time' in event) {
      const e = event as DailyEvent;
      if (e.time <= currentHour) {
        if (!currentActivity || e.time > currentActivity.time) {
          currentActivity = e;
        }
      }
    }
  }

  return currentActivity;
}

/**
 * Get NPC's next scheduled activity
 */
export function getNPCNextActivity(npc: Person, currentHour: number): DailyEvent | null {
  const plan = npc.dailyPlan ?? [];

  // Find the next activity after current time
  let nextActivity: DailyEvent | null = null;

  for (const event of plan) {
    if (typeof event === 'object' && event !== null && 'time' in event) {
      const e = event as DailyEvent;
      if (e.time > currentHour) {
        if (!nextActivity || e.time < nextActivity.time) {
          nextActivity = e;
        }
      }
    }
  }

  return nextActivity;
}

/**
 * Check if NPC is available for interaction at current time
 * NPCs are less available during work/school, sleeping, or commuting
 */
export function isNPCAvailable(npc: Person, currentHour: number): boolean {
  const activity = getNPCCurrentActivity(npc, currentHour);

  if (!activity) return true;

  // NPCs are not available during these activities
  const unavailableActivities = ['work', 'school', 'sleep', 'bed', 'commute', 'nap'];

  return !unavailableActivities.includes(activity.name);
}

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

export const npcScheduler = {
  generateNPCDailyPlan,
  handleNPCSchedules,
  updateNPCLocations,
  getNPCCurrentActivity,
  getNPCNextActivity,
  isNPCAvailable,
};
