/**
 * Game Engine Tests
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { Person } from '../../src/models/Person.js';
import { Player } from '../../src/models/Player.js';

describe('Game Engine', () => {
  let player: Player;

  beforeEach(() => {
    const character = new Person({
      id: 'main-char',
      firstname: 'Test',
      lastname: 'Player',
      sex: 'Male',
      ageYears: 20,
      ageDays: 7300, // 20 years
      status: 'alive',
      money: 5000,
      diamonds: 100,
      energy: 100,
      health: 100,
      happiness: 70,
      intelligence: 60,
      prestige: 20,
      education: 'High School',
    });

    player = new Player({
      userId: 'player-1',
      character,
      status: 'playing',
      date: '2024-06-15',
      hourOfDay: 8,
      minuteOfHour: 0,
      season: 'summer',
      gameSpeed: 1000,
    });
  });

  describe('Time Progression', () => {
    it('should advance time by minutes', () => {
      player.minuteOfHour = (player.minuteOfHour ?? 0) + 10;

      expect(player.minuteOfHour).toBe(10);
    });

    it('should wrap minutes to hours', () => {
      player.minuteOfHour = 55;
      player.minuteOfHour += 10;

      if (player.minuteOfHour >= 60) {
        player.hourOfDay = (player.hourOfDay ?? 0) + 1;
        player.minuteOfHour = player.minuteOfHour % 60;
      }

      expect(player.hourOfDay).toBe(9);
      expect(player.minuteOfHour).toBe(5);
    });

    it('should wrap hours to days', () => {
      player.hourOfDay = 23;
      player.hourOfDay += 2;

      if (player.hourOfDay >= 24) {
        player.c.ageDays = (player.c.ageDays ?? 0) + 1;
        player.hourOfDay = player.hourOfDay % 24;
      }

      expect(player.hourOfDay).toBe(1);
      expect(player.c.ageDays).toBe(7301);
    });

    it('should handle game speed multipliers', () => {
      const speeds = [10000, 1000, 500, 50, 20, 1];

      speeds.forEach(speed => {
        player.gameSpeed = speed;
        expect(player.gameSpeed).toBe(speed);
      });
    });
  });

  describe('Season Changes', () => {
    it('should cycle through seasons', () => {
      const seasons: ('spring' | 'summer' | 'autumn' | 'winter')[] = ['spring', 'summer', 'autumn', 'winter'];

      seasons.forEach((season) => {
        player.season = season;
        expect(player.season).toBe(season);
      });
    });

    it('should change season every 90 days', () => {
      const getSeason = (dayOfYear: number): string => {
        if (dayOfYear < 90) return 'winter';
        if (dayOfYear < 180) return 'spring';
        if (dayOfYear < 270) return 'summer';
        return 'autumn';
      };

      expect(getSeason(45)).toBe('winter');
      expect(getSeason(100)).toBe('spring');
      expect(getSeason(200)).toBe('summer');
      expect(getSeason(300)).toBe('autumn');
    });
  });

  describe('Energy System', () => {
    it('should consume energy for activities', () => {
      const activityCost = 20;
      player.c.energy = (player.c.energy ?? 100) - activityCost;

      expect(player.c.energy).toBe(80);
    });

    it('should regenerate energy over time', () => {
      player.c.energy = 50;
      const regenRate = 5; // per hour

      player.c.energy = Math.min(100, player.c.energy + regenRate);

      expect(player.c.energy).toBe(55);
    });

    it('should not exceed 100 energy', () => {
      player.c.energy = 95;
      player.c.energy = Math.min(100, player.c.energy + 10);

      expect(player.c.energy).toBe(100);
    });

    it('should not go below 0 energy', () => {
      player.c.energy = 10;
      player.c.energy = Math.max(0, player.c.energy - 50);

      expect(player.c.energy).toBe(0);
    });

    it('should block activities when no energy', () => {
      player.c.energy = 5;
      const activityCost = 20;

      const canDoActivity = (player.c.energy ?? 0) >= activityCost;

      expect(canDoActivity).toBe(false);
    });
  });

  describe('Health System', () => {
    it('should decrease health from age', () => {
      const ageHealthDecay = (age: number) => {
        if (age < 40) return 0;
        if (age < 60) return 0.1;
        if (age < 80) return 0.3;
        return 0.5;
      };

      expect(ageHealthDecay(30)).toBe(0);
      expect(ageHealthDecay(50)).toBe(0.1);
      expect(ageHealthDecay(70)).toBe(0.3);
      expect(ageHealthDecay(90)).toBe(0.5);
    });

    it('should increase health from activities', () => {
      player.c.health = 80;
      const exerciseBonus = 5;

      player.c.health = Math.min(100, player.c.health + exerciseBonus);

      expect(player.c.health).toBe(85);
    });

    it('should decrease health from habits', () => {
      player.c.health = 90;
      const habitDamage = 10;

      player.c.health = Math.max(0, player.c.health - habitDamage);

      expect(player.c.health).toBe(80);
    });

    it('should trigger death at 0 health', () => {
      player.c.health = 0;

      const isDead = player.c.health <= 0;

      expect(isDead).toBe(true);
    });
  });

  describe('Happiness System', () => {
    it('should increase from positive events', () => {
      player.c.happiness = 50;
      player.c.happiness = Math.min(100, player.c.happiness + 20);

      expect(player.c.happiness).toBe(70);
    });

    it('should decrease from negative events', () => {
      player.c.happiness = 70;
      player.c.happiness = Math.max(0, player.c.happiness - 30);

      expect(player.c.happiness).toBe(40);
    });

    it('should decay over time', () => {
      player.c.happiness = 80;
      const dailyDecay = 2;

      player.c.happiness = Math.max(0, player.c.happiness - dailyDecay);

      expect(player.c.happiness).toBe(78);
    });

    it('should affect event probabilities', () => {
      const getPositiveEventChance = (happiness: number) => {
        return 0.3 + (happiness / 100) * 0.4;
      };

      const lowHappinessChance = getPositiveEventChance(20);
      const highHappinessChance = getPositiveEventChance(80);

      expect(highHappinessChance).toBeGreaterThan(lowHappinessChance);
    });
  });

  describe('Money System', () => {
    it('should add money from job', () => {
      player.c.money = (player.c.money ?? 0) + 1000;

      expect(player.c.money).toBe(6000);
    });

    it('should subtract money from purchases', () => {
      player.c.money = (player.c.money ?? 0) - 500;

      expect(player.c.money).toBe(4500);
    });

    it('should allow negative money (debt)', () => {
      player.c.money = 100;
      player.c.money -= 500;

      expect(player.c.money).toBe(-400);
    });

    it('should calculate net worth', () => {
      const calculateNetWorth = (money: number, assets: number, debt: number) => {
        return money + assets - debt;
      };

      expect(calculateNetWorth(5000, 10000, 2000)).toBe(13000);
    });
  });

  describe('Prestige System', () => {
    it('should increase from achievements', () => {
      player.c.prestige = (player.c.prestige ?? 0) + 10;

      expect(player.c.prestige).toBe(30);
    });

    it('should affect job opportunities', () => {
      const getJobLevel = (prestige: number) => {
        if (prestige < 50) return 'entry';
        if (prestige < 200) return 'mid';
        if (prestige < 500) return 'senior';
        return 'executive';
      };

      expect(getJobLevel(20)).toBe('entry');
      expect(getJobLevel(100)).toBe('mid');
      expect(getJobLevel(300)).toBe('senior');
      expect(getJobLevel(600)).toBe('executive');
    });

    it('should have max prestige of 1000', () => {
      player.c.prestige = Math.min(1000, 1500);

      expect(player.c.prestige).toBe(1000);
    });
  });

  describe('Intelligence System', () => {
    it('should increase from education', () => {
      player.c.intelligence = (player.c.intelligence ?? 0) + 5;

      expect(player.c.intelligence).toBe(65);
    });

    it('should affect school performance', () => {
      const getGPA = (intelligence: number, effort: number) => {
        const base = (intelligence / 100) * 2 + (effort / 100) * 2;
        return Math.min(4.0, base);
      };

      expect(getGPA(60, 80)).toBeCloseTo(2.8);
      expect(getGPA(100, 100)).toBe(4.0);
    });

    it('should be capped at 100', () => {
      player.c.intelligence = Math.min(100, 120);

      expect(player.c.intelligence).toBe(100);
    });
  });

  describe('Game State', () => {
    it('should handle pause state', () => {
      player.status = 'paused';

      expect(player.status).toBe('paused');
    });

    it('should handle playing state', () => {
      player.status = 'playing';

      expect(player.status).toBe('playing');
    });

    it('should handle dead state', () => {
      player.status = 'dead';

      expect(player.status).toBe('dead');
    });

    it('should handle creating state', () => {
      player.status = 'creating';

      expect(player.status).toBe('creating');
    });
  });

  describe('Event Probability', () => {
    it('should calculate event probability', () => {
      const baseProb = 0.1;
      const randomChance = Math.random();

      const eventTriggered = randomChance < baseProb;

      expect(typeof eventTriggered).toBe('boolean');
    });

    it('should weight probabilities by time of day', () => {
      const getTimeWeight = (hour: number) => {
        if (hour >= 6 && hour < 12) return 1.2; // Morning boost
        if (hour >= 12 && hour < 18) return 1.0; // Normal
        if (hour >= 18 && hour < 22) return 0.8; // Evening slower
        return 0.5; // Night minimal
      };

      expect(getTimeWeight(8)).toBe(1.2);
      expect(getTimeWeight(14)).toBe(1.0);
      expect(getTimeWeight(20)).toBe(0.8);
      expect(getTimeWeight(2)).toBe(0.5);
    });
  });

  describe('Batched Updates', () => {
    it('should batch multiple stat changes', () => {
      const updates = {
        energy: -20,
        happiness: 10,
        money: 100,
      };

      player.c.energy = (player.c.energy ?? 100) + updates.energy;
      player.c.happiness = (player.c.happiness ?? 50) + updates.happiness;
      player.c.money = (player.c.money ?? 0) + updates.money;

      expect(player.c.energy).toBe(80);
      expect(player.c.happiness).toBe(80);
      expect(player.c.money).toBe(5100);
    });
  });

  describe('Save/Load State', () => {
    it('should serialize player state', () => {
      const serialized = JSON.stringify(player);

      expect(typeof serialized).toBe('string');
      expect(serialized.length).toBeGreaterThan(0);
    });

    it('should deserialize player state', () => {
      const serialized = JSON.stringify(player.toJSON());
      const deserialized = JSON.parse(serialized);

      expect(deserialized.userId).toBe('player-1');
      expect(deserialized.c.firstname).toBe('Test');
    });
  });
});
