/**
 * Player Data Repair Script
 *
 * Fixes corrupted player data:
 * - Clamps stats to valid ranges (0-100)
 * - Enforces relationship exclusivity
 * - Cleans up stale pending events
 * - Sets up missing education data
 *
 * Usage: npx tsx scripts/repairPlayerData.ts <playerId>
 */

import mysql from 'mysql2/promise';
import dotenv from 'dotenv';

dotenv.config();

const STAT_BOUNDS = {
  energy: { min: 0, max: 100, default: 100 },
  happiness: { min: 0, max: 100, default: 50 },
  health: { min: 0, max: 100, default: 100 },
  social: { min: 0, max: 100, default: 50 },
  stress: { min: 0, max: 100, default: 0 },
  hunger: { min: 0, max: 100, default: 0 },
  thirst: { min: 0, max: 100, default: 0 },
  affinity: { min: 0, max: 100, default: 50 },
};

const ROMANTIC_TYPES = ['partner', 'girlfriend', 'boyfriend', 'dating', 'spouse', 'wife', 'husband', 'fiancé', 'fiancée', 'engaged', 'dating_match'];

function clamp(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, value));
}

function clampPersonStats(person: any): void {
  for (const [stat, bounds] of Object.entries(STAT_BOUNDS)) {
    if (person[stat] !== undefined) {
      const oldValue = person[stat];
      person[stat] = clamp(person[stat], bounds.min, bounds.max);
      if (oldValue !== person[stat]) {
        console.log(`  Fixed ${person.firstname || 'player'}.${stat}: ${oldValue} -> ${person[stat]}`);
      }
    }
  }
}

function enforceRelationshipExclusivity(player: any): void {
  if (!player.r) return;

  // Find all characters with romantic relationships
  const romanticPartners = player.r.filter((p: any) =>
    p.relationships?.some((r: string) => ROMANTIC_TYPES.includes(r.toLowerCase()))
  );

  if (romanticPartners.length <= 1) {
    console.log('  No relationship exclusivity issues');
    return;
  }

  console.log(`  Found ${romanticPartners.length} romantic relationships - enforcing exclusivity`);

  // Sort by familiarity (higher = more established relationship)
  romanticPartners.sort((a: any, b: any) => (b.familiarity ?? 0) - (a.familiarity ?? 0));

  // Keep the first one (highest familiarity), downgrade others to ex
  const toKeep = romanticPartners[0];
  console.log(`  Keeping: ${toKeep.firstname} (familiarity: ${toKeep.familiarity})`);

  for (let i = 1; i < romanticPartners.length; i++) {
    const partner = romanticPartners[i];
    console.log(`  Downgrading to ex: ${partner.firstname}`);

    // Remove romantic relationship types
    if (partner.relationships) {
      partner.relationships = partner.relationships.filter(
        (r: string) => !ROMANTIC_TYPES.includes(r.toLowerCase())
      );
      // Add 'ex' if not already there
      if (!partner.relationships.includes('ex')) {
        partner.relationships.push('ex');
      }
    }
  }
}

function cleanStalePendingEvents(player: any): number {
  if (!player.pendingConversationEvents) return 0;

  const originalCount = player.pendingConversationEvents.length;

  player.pendingConversationEvents = player.pendingConversationEvents.filter((event: any) => {
    // Check if character exists
    const character = player.r?.find((p: any) => p.id === event.characterId);
    if (!character) {
      console.log(`  Removing event for missing character: ${event.characterName}`);
      return false;
    }

    // Remove relationship events for ex partners
    if (character.relationships?.includes('ex')) {
      const relationshipEventTypes = ['relationship_accepted', 'confession', 'date_request'];
      if (relationshipEventTypes.includes(event.type)) {
        console.log(`  Removing ${event.type} event for ex: ${character.firstname}`);
        return false;
      }
    }

    return true;
  });

  return originalCount - player.pendingConversationEvents.length;
}

function setupEducation(player: any): void {
  const person = player.c;
  const age = person.ageYears ?? 0;

  if (age < 5) {
    console.log('  Player is too young for school');
    return;
  }

  // Check if education is already set
  if (person.school || person.high_school || person.college) {
    console.log('  Education already set');
    return;
  }

  console.log(`  Setting up education for age ${age}`);

  // Elementary School (Ages 5-13)
  if (age >= 5 && age < 14) {
    person.school = {
      name: 'Lincoln Elementary School',
      type: 'elementary',
    };
    person.grade = getGradeForAge(age);
    console.log(`  Enrolled in elementary school, grade: ${person.grade}`);
  }
  // High School (Ages 14-17)
  else if (age >= 14 && age < 18) {
    person.high_school = {
      name: 'Central High School',
      type: 'high_school',
    };
    person.grade = getGradeForAge(age);
    person.occupation = 'student';
    console.log(`  Enrolled in high school, grade: ${person.grade}`);
  }
  // College (Ages 18+)
  else if (age >= 18) {
    console.log('  Player is adult - not auto-enrolling in education');
  }
}

function getGradeForAge(age: number): string {
  const gradeMap: Record<number, string> = {
    5: 'kindergarten',
    6: '1st',
    7: '2nd',
    8: '3rd',
    9: '4th',
    10: '5th',
    11: '6th',
    12: '7th',
    13: '8th',
    14: '9th',
    15: '10th',
    16: '11th',
    17: '12th',
  };
  return gradeMap[age] ?? 'unknown';
}

async function repairPlayer(playerId: string): Promise<void> {
  const conn = await mysql.createConnection({
    host: process.env.DB_HOST || 'localhost',
    user: process.env.DB_USER || 'root',
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME || 'lifesim',
  });

  try {
    console.log(`\nLoading player: ${playerId}`);
    const [rows] = await conn.execute('SELECT data FROM players WHERE id = ?', [playerId]) as any;

    if (!rows || rows.length === 0) {
      console.log('Player not found!');
      return;
    }

    const player = JSON.parse(rows[0].data);
    console.log(`Found: ${player.c?.firstname} ${player.c?.lastname}, Age ${player.c?.ageYears}`);

    console.log('\n=== REPAIRING STATS ===');
    // Fix main character stats
    clampPersonStats(player.c);

    // Fix all relationship character stats
    if (player.r) {
      for (const person of player.r) {
        clampPersonStats(person);
      }
    }

    console.log('\n=== ENFORCING RELATIONSHIP EXCLUSIVITY ===');
    enforceRelationshipExclusivity(player);

    console.log('\n=== CLEANING STALE EVENTS ===');
    const removedEvents = cleanStalePendingEvents(player);
    console.log(`  Removed ${removedEvents} stale events`);

    console.log('\n=== SETTING UP EDUCATION ===');
    setupEducation(player);

    // Save repaired data
    console.log('\n=== SAVING REPAIRED DATA ===');
    await conn.execute('UPDATE players SET data = ?, updated_at = NOW() WHERE id = ?', [
      JSON.stringify(player),
      playerId,
    ]);
    console.log('Player data repaired and saved!');

    // Print final stats
    console.log('\n=== FINAL STATUS ===');
    console.log(`Name: ${player.c?.firstname} ${player.c?.lastname}`);
    console.log(`Age: ${player.c?.ageYears}`);
    console.log(`Energy: ${player.c?.energy}`);
    console.log(`Happiness: ${player.c?.happiness}`);
    console.log(`Stress: ${player.c?.stress}`);
    console.log(`Social: ${player.c?.social}`);
    console.log(`School: ${player.c?.school?.name || player.c?.high_school?.name || 'none'}`);
    console.log(`Grade: ${player.c?.grade || 'none'}`);
    console.log(`Pending events: ${player.pendingConversationEvents?.length || 0}`);

    // Show romantic relationships
    const romanticPartners = player.r?.filter((p: any) =>
      p.relationships?.some((r: string) => ROMANTIC_TYPES.includes(r.toLowerCase()))
    );
    console.log(`Romantic partners: ${romanticPartners?.length || 0}`);
    romanticPartners?.forEach((p: any) => {
      console.log(`  - ${p.firstname}: ${p.relationships?.join(', ')}`);
    });

  } finally {
    await conn.end();
  }
}

// Main
const playerId = process.argv[2];
if (!playerId) {
  console.log('Usage: npx tsx scripts/repairPlayerData.ts <playerId>');
  console.log('Example: npx tsx scripts/repairPlayerData.ts 46D21BF3-1764-4BC9-AA24-1EFFE3FEB07B');
  process.exit(1);
}

repairPlayer(playerId).catch(console.error);
