/**
 * Person model - represents a character in the game
 * Ported from Python personClass
 */

export type Sex = 'Male' | 'Female';
export type PersonStatus = 'alive' | 'dead' | 'unknown';

export interface Item {
  id: string;
  name: string;
  type?: string;
  value?: number;
  /**
   * Recurring weekly upkeep cost for owning this item (prestige/lifestyle sink).
   * Summed into weekly expenses by the finance system so luxury items create
   * ongoing cashflow pressure instead of being a one-time purchase.
   */
  weeklyUpkeep?: number;
}

export interface Activity {
  id: string;
  title: string;
  type: string;
  energyModifier?: number;
}

/**
 * Schedule interface for person activities
 * Most fields are optional to support different schedule types
 */
export interface Schedule {
  id?: string;
  title?: string;
  location?: string;
  duration?: number;
  executions?: number;
  days?: {
    daysOfWeek?: string[];
    hour?: number;
  };
  type?: string;
  energyModifier?: number;
}

export interface ChildPerson {
  id: string;
  firstname: string;
  lastname?: string;
  ageDays: number;
  ageYears: number;
  sex: Sex;
  image?: string;
}

export interface RelationshipPerson {
  id: string;
  firstname: string;
  lastname?: string;
  ageYears?: number;
  sex?: Sex;
  image?: string;
  title?: string;
  affinity?: number;
  familyLevel?: number;
  relationships?: string[];
}

export interface PersonData {
  id: string;
  firstname: string;
  lastname: string;
  sex: Sex;
  image?: string;

  // Status
  status?: PersonStatus;
  relationships?: string[];  // Relationship types: 'friend', 'classmate', 'partner', etc.
  familiarity?: number;      // How well they know the player (0-100)

  // Age
  ageDays?: number;
  ageYears?: number;
  ageHours?: number;
  birthday?: string;

  // Stats
  mood?: string;  // "Calm", "Stressed", "Exhausted", "Fulfilled", "Depressed", "Happy"
  affinity?: number;
  money?: number;
  diamonds?: number;
  prestige?: number;
  happiness?: number;
  health?: number;
  energy?: number;
  intelligence?: number;
  social?: number;
  stress?: number;
  creativity?: number;
  hunger?: number;     // 0-100, higher = more hungry
  thirst?: number;     // 0-100, higher = more thirsty
  weight?: number;     // kg, default 55
  weightType?: string; // from getWeightType()
  pronoun?: string;    // "He" or "She"

  // Location and status
  location?: string;
  occupation?: string;
  education?: string;
  job?: any;
  ownsBusiness?: boolean;

  // Physical attributes
  braces?: boolean;
  glasses?: boolean;
  canDrive?: boolean;

  // Relationships
  partner?: string;
  relationship?: string | null;  // ID of the active RomanticRelationship in player.relData (if any)
  firstCrush?: string;
  sexualOrientation?: string;
  children?: ChildPerson[];
  tryingForChild?: boolean;
  pregnant?: boolean;
  /** In-game day (ageDays) the current pregnancy reaches term and a child is born. */
  pregnancyDueDay?: number;
  engaged?: boolean;

  // Activities and items
  activities?: any[];
  activityRecords?: any[];
  items?: Item[];

  // Social
  hasSocialMedia?: boolean;

  // Financial
  hasBankAccount?: boolean;

  // Education/Test scores
  actScore?: number;
  satScore?: number;
  major?: string;
  college?: any;

  // Education locations
  elementary_school?: any;
  high_school?: any;
  current_education?: any;

  // Profile
  bio?: string;
  interests?: string[];
  traits?: string[];
  compatibilityScore?: number;

  // Additional flags
  immunizations?: boolean;
  likes?: string[];
  dislikes?: string[];

  // Game loop properties
  calcEnergy?: number;
  peakEnergy?: number;
  salary?: number;
  schedules?: Schedule[];
  intraDayMessage?: string;
  lastIntraDayMessage?: string;
  dailyPlan?: Schedule[];
  deathChance?: number;
  familyLevel?: number;

  // Relationship neglect tracking (T010d "relationships matter").
  // lastPositiveInteraction: in-game date (MM-DD or the player.date string) of the
  //   most recent positive interaction (date night, gift, warm conversation). Used
  //   to drive romantic neglect-decay and the "drifting apart" affinity erosion.
  // neglectAtRiskNotified: set once a once-high-affinity relationship has crossed
  //   the at-risk threshold so the "relationship at risk" nudge fires only once per
  //   decline (reset when affinity recovers above the high threshold).
  // parentingInvestment: running tally of parenting effort (affinity + interaction
  //   frequency) accumulated while a child is a minor; consumed when the child
  //   becomes an adult to shape their adult affinity/contact.
  lastPositiveInteraction?: string;
  neglectAtRiskNotified?: boolean;
  parentingInvestment?: number;
  childMaturedOutcome?: 'supportive' | 'neutral' | 'distant';
  // High-water mark: set once this NPC's affinity has ever reached the "high"
  // band (>= 70). Gates the "relationship at risk" nudge so it only fires for an
  // NPC the player once felt strongly about, after their affinity decays away.
  affinityWasHigh?: boolean;

  // Last in-game date the player and this NPC had a conversation. Read by the
  // NPC-initiative time-gap trigger; persisted so the gap survives reconnects.
  lastConversationDate?: string;

  // Player agency: pending player-chosen activity id for the upcoming evening/
  // weekend slot. Honored by getDailyPlan/getRandomEveningActivity instead of
  // the random roll, then consumed. Persists through save/load so a queued
  // override survives reconnects.
  plannedActivity?: string;

  // Per-activity skill levels for the proactive agency loop (study / exercise /
  // socialize / sideHustle / hobby). Keyed by PlayerActivity id (e.g.
  // skills.study). Incremented each time the player performs that activity and
  // used to scale the activity's stat gains along a real progression curve.
  // Persists through save/load so practice compounds across sessions.
  skills?: Record<string, number>;

  // Habits and spending
  spendingHabits?: 'frugal' | 'normal' | 'extravagant';
  habits?: Array<{
    name: string;
    description?: string;
    type?: 'habit';
    habitType?: 'positive' | 'negative';
    status: 'active' | 'quitting';
    quitProgress?: number;
    daysQuit?: number;
  }>;

  // One-time events
  oneTimeEvents?: Array<{
    id: string;
    date: string;
    hour: number;
    message: string;
    completionFunc?: string;
    runFunc?: (player: unknown) => void;
  }>;

  // Messaging style traits (for NPC conversations)
  messaging_traits?: {
    verbosity: number;        // 0-100 - how long messages tend to be
    inquisitiveness: number;  // 0-100 - how often asks questions
    expressiveness: number;   // 0-100 - how animated/enthusiastic
    responsiveness: number;   // 0-100 - how fully engages with messages
    openness: number;         // 0-100 - how much shares personal details
    emoji_usage: number;      // 0-100 - how often uses emojis
    formality: number;        // 0-100 - how formal/proper the language
    response_timing: number;  // 0-100 - how quickly responds
  };

  // Messaging patterns
  messaging_patterns?: {
    time_of_day_preference: 'morning' | 'night' | 'neutral';
    weekend_availability: number;  // 0-100
    typing_style: 'proper' | 'casual' | 'chaotic';
  };
}

export class Person implements PersonData {
  id: string;
  firstname: string;
  lastname: string;
  sex: Sex;
  image: string;

  // Status
  status: PersonStatus;
  relationships: string[];
  familiarity: number;

  // Age
  ageDays: number;
  ageYears: number;
  ageHours: number;
  birthday: string;

  // Stats
  mood: string;  // "Calm", "Stressed", "Exhausted", "Fulfilled", "Depressed", "Happy"
  affinity: number;
  money: number;
  diamonds: number;
  prestige: number;
  happiness: number;
  health: number;
  energy: number;
  intelligence: number;
  social: number;
  stress: number;
  creativity: number;
  hunger: number;      // 0-100, higher = more hungry
  thirst: number;      // 0-100, higher = more thirsty
  weight: number;      // kg
  weightType: string;  // from getWeightType()
  pronoun: string;     // "He" or "She"

  // Location and status
  location: string;
  occupation: string;
  education: string;
  job?: any;
  ownsBusiness: boolean;

  // Physical attributes
  braces: boolean;
  glasses: boolean;
  canDrive: boolean;

  // Relationships
  partner?: string;
  relationship?: string | null;  // ID of the active RomanticRelationship in player.relData (if any)
  firstCrush?: string;
  sexualOrientation: string;
  children: ChildPerson[];
  tryingForChild?: boolean;
  pregnant?: boolean;
  pregnancyDueDay?: number;
  engaged?: boolean;

  // Activities and items
  activities: any[];
  activityRecords: any[];
  items: Item[];

  // Social
  hasSocialMedia: boolean;

  // Financial
  hasBankAccount: boolean;

  // Education/Test scores
  actScore?: number;
  satScore?: number;
  major?: string;
  college?: any;

  // Education locations
  elementary_school?: any;
  high_school?: any;
  current_education?: any;

  // Profile
  bio: string;
  interests: string[];
  traits: string[];
  compatibilityScore: number;

  // Additional flags
  immunizations?: boolean;
  likes: string[];
  dislikes: string[];

  // Game loop properties
  calcEnergy: number;
  peakEnergy: number;
  salary: number;
  schedules: Schedule[];
  intraDayMessage: string;
  lastIntraDayMessage: string;
  dailyPlan: Schedule[];
  deathChance: number;
  familyLevel: number;

  // Relationship neglect tracking (see PersonData for semantics).
  lastPositiveInteraction?: string;
  neglectAtRiskNotified?: boolean;
  parentingInvestment?: number;
  childMaturedOutcome?: 'supportive' | 'neutral' | 'distant';
  affinityWasHigh?: boolean;
  lastConversationDate?: string;

  // Player agency: pending player-chosen activity id (see PersonData).
  plannedActivity?: string;

  // Per-activity skill levels (see PersonData).
  skills: Record<string, number>;

  // Habits and spending
  spendingHabits: 'frugal' | 'normal' | 'extravagant';
  habits: Array<{
    name: string;
    description?: string;
    type?: 'habit';
    habitType?: 'positive' | 'negative';
    status: 'active' | 'quitting';
    quitProgress?: number;
    daysQuit?: number;
  }>;

  // One-time events
  oneTimeEvents: Array<{
    id: string;
    date: string;
    hour: number;
    message: string;
    completionFunc?: string;
    runFunc?: (player: unknown) => void;
  }>;

  // Messaging style traits (for NPC conversations)
  messaging_traits?: {
    verbosity: number;
    inquisitiveness: number;
    expressiveness: number;
    responsiveness: number;
    openness: number;
    emoji_usage: number;
    formality: number;
    response_timing: number;
  };

  // Messaging patterns
  messaging_patterns?: {
    time_of_day_preference: 'morning' | 'night' | 'neutral';
    weekend_availability: number;
    typing_style: 'proper' | 'casual' | 'chaotic';
  };

  constructor(data: PersonData) {
    this.id = data.id;
    this.firstname = data.firstname;
    this.lastname = data.lastname;
    this.sex = data.sex;
    this.image = data.image ?? '';

    // Status
    this.status = data.status ?? 'alive';
    this.relationships = data.relationships ?? [];
    this.familiarity = data.familiarity ?? 0;

    // Age
    this.ageDays = data.ageDays ?? 0;
    this.ageYears = data.ageYears ?? Math.floor((data.ageDays ?? 0) / 365);
    this.ageHours = data.ageHours ?? (this.ageDays * 24);
    this.birthday = data.birthday ?? '';

    // Stats
    this.mood = data.mood ?? 'Calm';
    this.affinity = data.affinity ?? 50;
    this.money = data.money ?? 0;
    this.diamonds = data.diamonds ?? 0;
    this.prestige = data.prestige ?? 0;
    this.happiness = data.happiness ?? 50;
    this.health = data.health ?? 100;
    this.energy = data.energy ?? 100;
    this.intelligence = data.intelligence ?? 50;
    this.social = data.social ?? 50;
    this.stress = data.stress ?? 0;
    this.creativity = data.creativity ?? 50;
    this.hunger = data.hunger ?? 0;
    this.thirst = data.thirst ?? 0;
    this.weight = data.weight ?? 55;
    this.weightType = data.weightType ?? 'normal';
    this.pronoun = data.pronoun ?? (data.sex === 'Male' ? 'He' : 'She');

    // Location and status
    this.location = data.location ?? 'home';
    this.occupation = data.occupation ?? '';
    this.education = data.education ?? '';
    this.job = data.job;
    this.ownsBusiness = data.ownsBusiness ?? false;

    // Physical attributes
    this.braces = data.braces ?? false;
    this.glasses = data.glasses ?? false;
    this.canDrive = data.canDrive ?? false;

    // Relationships
    this.partner = data.partner;
    this.relationship = data.relationship;
    this.firstCrush = data.firstCrush;
    this.sexualOrientation = data.sexualOrientation ?? '';
    this.children = data.children ?? [];
    // Pregnancy state must survive the DB round-trip (a pregnancy spans ~9
    // in-game months across many save/load cycles). These were previously
    // emitted by toJSON but never restored here, so they reset on reload.
    this.tryingForChild = data.tryingForChild;
    this.pregnant = data.pregnant;
    this.pregnancyDueDay = data.pregnancyDueDay;

    // Activities and items
    this.activities = data.activities ?? [];
    this.activityRecords = data.activityRecords ?? [];
    this.items = data.items ?? [];

    // Social
    this.hasSocialMedia = data.hasSocialMedia ?? false;

    // Financial
    this.hasBankAccount = data.hasBankAccount ?? false;

    // Profile
    this.bio = data.bio ?? '';
    this.interests = data.interests ?? [];
    this.traits = data.traits ?? [];
    this.compatibilityScore = data.compatibilityScore ?? 0;

    // Additional flags
    this.likes = data.likes ?? [];
    this.dislikes = data.dislikes ?? [];

    // Game loop properties
    this.calcEnergy = data.calcEnergy ?? this.energy;
    this.peakEnergy = data.peakEnergy ?? 0;
    this.salary = data.salary ?? 0;
    this.schedules = data.schedules ?? [];
    this.intraDayMessage = data.intraDayMessage ?? '';
    this.lastIntraDayMessage = data.lastIntraDayMessage ?? '';
    this.dailyPlan = data.dailyPlan ?? [];
    this.deathChance = data.deathChance ?? 0;
    this.familyLevel = data.familyLevel ?? 0;
    this.lastPositiveInteraction = data.lastPositiveInteraction;
    this.neglectAtRiskNotified = data.neglectAtRiskNotified;
    this.parentingInvestment = data.parentingInvestment;
    this.childMaturedOutcome = data.childMaturedOutcome;
    this.affinityWasHigh = data.affinityWasHigh ?? (typeof data.affinity === 'number' && data.affinity >= 70);
    this.lastConversationDate = data.lastConversationDate;
    this.plannedActivity = data.plannedActivity;
    this.skills = data.skills ?? {};

    // Habits and spending
    this.spendingHabits = data.spendingHabits ?? 'normal';
    this.habits = data.habits ?? [];

    // One-time events
    this.oneTimeEvents = data.oneTimeEvents ?? [];

    // Messaging style (optional, initialized on first conversation if not provided)
    this.messaging_traits = data.messaging_traits;
    this.messaging_patterns = data.messaging_patterns;
  }

  get fullName(): string {
    return `${this.firstname} ${this.lastname}`;
  }

  get healthPercentage(): number {
    return Math.max(0, Math.min(100, this.health));
  }

  toJSON(): PersonData {
    return {
      id: this.id,
      firstname: this.firstname,
      lastname: this.lastname,
      sex: this.sex,
      image: this.image,
      status: this.status,
      relationships: this.relationships,
      familiarity: this.familiarity,
      ageDays: this.ageDays,
      ageYears: this.ageYears,
      ageHours: this.ageHours,
      birthday: this.birthday,
      mood: this.mood,
      affinity: this.affinity,
      money: this.money,
      diamonds: this.diamonds,
      prestige: this.prestige,
      happiness: this.happiness,
      health: this.health,
      energy: this.energy,
      intelligence: this.intelligence,
      social: this.social,
      stress: this.stress,
      creativity: this.creativity,
      hunger: this.hunger,
      thirst: this.thirst,
      weight: this.weight,
      weightType: this.weightType,
      pronoun: this.pronoun,
      location: this.location,
      occupation: this.occupation,
      education: this.education,
      job: this.job,
      ownsBusiness: this.ownsBusiness,
      braces: this.braces,
      glasses: this.glasses,
      canDrive: this.canDrive,
      partner: this.partner,
      relationship: this.relationship,
      firstCrush: this.firstCrush,
      sexualOrientation: this.sexualOrientation,
      children: this.children,
      tryingForChild: this.tryingForChild,
      pregnant: this.pregnant,
      pregnancyDueDay: this.pregnancyDueDay,
      engaged: this.engaged,
      activities: this.activities,
      activityRecords: this.activityRecords,
      items: this.items,
      hasSocialMedia: this.hasSocialMedia,
      hasBankAccount: this.hasBankAccount,
      actScore: this.actScore,
      satScore: this.satScore,
      major: this.major,
      college: this.college,
      elementary_school: this.elementary_school,
      high_school: this.high_school,
      current_education: this.current_education,
      bio: this.bio,
      interests: this.interests,
      traits: this.traits,
      compatibilityScore: this.compatibilityScore,
      immunizations: this.immunizations,
      likes: this.likes,
      dislikes: this.dislikes,
      calcEnergy: this.calcEnergy,
      peakEnergy: this.peakEnergy,
      salary: this.salary,
      schedules: this.schedules,
      intraDayMessage: this.intraDayMessage,
      lastIntraDayMessage: this.lastIntraDayMessage,
      dailyPlan: this.dailyPlan,
      deathChance: this.deathChance,
      familyLevel: this.familyLevel,
      lastPositiveInteraction: this.lastPositiveInteraction,
      neglectAtRiskNotified: this.neglectAtRiskNotified,
      parentingInvestment: this.parentingInvestment,
      childMaturedOutcome: this.childMaturedOutcome,
      affinityWasHigh: this.affinityWasHigh,
      lastConversationDate: this.lastConversationDate,
      plannedActivity: this.plannedActivity,
      skills: this.skills,
      spendingHabits: this.spendingHabits,
      habits: this.habits,
      oneTimeEvents: this.oneTimeEvents,
      messaging_traits: this.messaging_traits,
      messaging_patterns: this.messaging_patterns,
    };
  }
}
