/**
 * Event System Tests
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { Person } from '../../src/models/Person.js';
import { Player } from '../../src/models/Player.js';
import { MeditationEvent } from '../../src/events/activities/hobbies.js';

// Import event types for testing structure
interface GameEvent {
  id: string;
  name: string;
  description?: string;
  trigger?: (player: Player) => boolean;
  execute?: (player: Player) => any;
  minAge?: number;
  maxAge?: number;
  probability?: number;
  category?: string;
}

describe('Event System', () => {
  let testPlayer: Player;

  beforeEach(() => {
    const character = new Person({
      id: 'char-1',
      firstname: 'Test',
      ageYears: 20,
      money: 1000,
      diamonds: 50,
      energy: 80,
      health: 90,
      happiness: 70,
      intelligence: 60,
      prestige: 30,
      education: 'High School',
    });

    testPlayer = new Player({
      userId: 'player-1',
      character: character,
      r: [],
      status: 'playing',
      date: '2024-06-15',
      hourOfDay: 10,
      season: 'summer',
    });
  });

  describe('Event structure', () => {
    it('should have required properties', () => {
      const mockEvent: GameEvent = {
        id: 'test-event-1',
        name: 'Test Event',
        description: 'A test event',
        probability: 0.5,
      };

      expect(mockEvent.id).toBeDefined();
      expect(mockEvent.name).toBeDefined();
    });

    it('should have age restrictions', () => {
      const childEvent: GameEvent = {
        id: 'child-event',
        name: 'Childhood Event',
        minAge: 0,
        maxAge: 12,
      };

      expect(childEvent.minAge).toBe(0);
      expect(childEvent.maxAge).toBe(12);
    });

    it('should have probability', () => {
      const rareEvent: GameEvent = {
        id: 'rare-event',
        name: 'Rare Event',
        probability: 0.01,
      };

      expect(rareEvent.probability).toBeLessThan(1);
      expect(rareEvent.probability).toBeGreaterThan(0);
    });
  });

  describe('Event triggers', () => {
    it('should check age requirements', () => {
      const checkAge = (player: Player, minAge: number, maxAge: number) => {
        const age = player.c.ageYears ?? 0;
        return age >= minAge && age <= maxAge;
      };

      expect(checkAge(testPlayer, 18, 25)).toBe(true);
      expect(checkAge(testPlayer, 25, 30)).toBe(false);
    });

    it('should check education requirements', () => {
      const checkEducation = (player: Player, required: string) => {
        return player.c.education === required;
      };

      expect(checkEducation(testPlayer, 'High School')).toBe(true);
      expect(checkEducation(testPlayer, 'College')).toBe(false);
    });

    it('should check resource requirements', () => {
      const checkResources = (player: Player, minMoney: number, minEnergy: number) => {
        return (player.c.money ?? 0) >= minMoney && (player.c.energy ?? 0) >= minEnergy;
      };

      expect(checkResources(testPlayer, 500, 50)).toBe(true);
      expect(checkResources(testPlayer, 2000, 50)).toBe(false);
    });

    it('should check time of day', () => {
      const checkTimeOfDay = (player: Player, minHour: number, maxHour: number) => {
        const hour = player.hourOfDay ?? 0;
        return hour >= minHour && hour <= maxHour;
      };

      expect(checkTimeOfDay(testPlayer, 8, 18)).toBe(true);
      expect(checkTimeOfDay(testPlayer, 20, 23)).toBe(false);
    });

    it('should check season', () => {
      const checkSeason = (player: Player, season: string) => {
        return player.season === season;
      };

      expect(checkSeason(testPlayer, 'summer')).toBe(true);
      expect(checkSeason(testPlayer, 'winter')).toBe(false);
    });
  });

  describe('Event categories', () => {
    const categories = [
      'childhood', 'adolescence', 'adulthood',
      'education', 'career', 'social',
      'health', 'random', 'holiday'
    ];

    categories.forEach(category => {
      it(`should support ${category} category`, () => {
        const event: GameEvent = {
          id: `${category}-test`,
          name: `${category} Test Event`,
          category,
        };

        expect(event.category).toBe(category);
      });
    });
  });

  describe('Event effects', () => {
    it('should modify player stats', () => {
      const initialHealth = testPlayer.c.health!;
      testPlayer.c.health = initialHealth - 10;

      expect(testPlayer.c.health).toBe(initialHealth - 10);
    });

    it('should modify resources', () => {
      const initialMoney = testPlayer.c.money!;
      testPlayer.c.money = initialMoney + 500;

      expect(testPlayer.c.money).toBe(initialMoney + 500);
    });

    it('should not exceed stat maximums', () => {
      testPlayer.c.health = 150;
      const cappedHealth = Math.min(testPlayer.c.health, 100);

      expect(cappedHealth).toBe(100);
    });

    it('should not go below stat minimums', () => {
      testPlayer.c.happiness = -50;
      const flooredHappiness = Math.max(testPlayer.c.happiness, 0);

      expect(flooredHappiness).toBe(0);
    });
  });

  describe('Childhood events', () => {
    beforeEach(() => {
      testPlayer.c.ageYears = 5;
    });

    it('should trigger for appropriate age', () => {
      const isChild = (testPlayer.c.ageYears ?? 0) < 12;
      expect(isChild).toBe(true);
    });

    it('should not require job', () => {
      expect(testPlayer.c.occupation).toBe(''); // defaults to empty string
    });
  });

  describe('Adolescence events', () => {
    beforeEach(() => {
      testPlayer.c.ageYears = 15;
    });

    it('should trigger for teenage years', () => {
      const age = testPlayer.c.ageYears ?? 0;
      const isAdolescent = age >= 12 && age < 18;

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

    it('should be in school', () => {
      testPlayer.c.education = 'High School';
      expect(testPlayer.c.education).toBe('High School');
    });
  });

  describe('Adulthood events', () => {
    beforeEach(() => {
      testPlayer.c.ageYears = 30;
    });

    it('should trigger for adult age', () => {
      const isAdult = (testPlayer.c.ageYears ?? 0) >= 18;
      expect(isAdult).toBe(true);
    });

    it('should allow career events', () => {
      testPlayer.c.occupation = 'Developer';
      expect(testPlayer.c.occupation).toBeDefined();
    });
  });

  describe('Random events', () => {
    it('should have probability between 0 and 1', () => {
      const probability = 0.3;
      expect(probability).toBeGreaterThan(0);
      expect(probability).toBeLessThanOrEqual(1);
    });

    it('should simulate random occurrence', () => {
      const probability = 0.5;
      let triggered = 0;
      const trials = 100;

      for (let i = 0; i < trials; i++) {
        if (Math.random() < probability) {
          triggered++;
        }
      }

      // Should be roughly 50% with some variance
      expect(triggered).toBeGreaterThan(20);
      expect(triggered).toBeLessThan(80);
    });
  });

  describe('School year events', () => {
    it('should track school progress', () => {
      testPlayer.c.education = 'High School';
      expect(testPlayer.c.education).toBeDefined();
    });

    it('should handle graduation', () => {
      const isGraduationAge = (testPlayer.c.ageYears ?? 0) >= 18;

      if (isGraduationAge && testPlayer.c.education === 'High School') {
        // Can graduate
        expect(true).toBe(true);
      }
    });
  });

  describe('Health events', () => {
    it('should affect health stat', () => {
      const healthEvent = {
        execute: (player: Player) => {
          player.c.health = Math.max(0, (player.c.health ?? 100) - 20);
          return { healthChange: -20 };
        },
      };

      const result = healthEvent.execute(testPlayer);
      expect(result.healthChange).toBe(-20);
    });

    it('should consider age for health events', () => {
      const getHealthRisk = (age: number) => {
        if (age < 30) return 0.1;
        if (age < 50) return 0.2;
        if (age < 70) return 0.4;
        return 0.6;
      };

      expect(getHealthRisk(20)).toBe(0.1);
      expect(getHealthRisk(60)).toBe(0.4);
    });
  });

  describe('Holiday events', () => {
    const holidays = [
      { name: 'New Year', month: 1, day: 1 },
      { name: 'Valentine\'s Day', month: 2, day: 14 },
      { name: 'Halloween', month: 10, day: 31 },
      { name: 'Christmas', month: 12, day: 25 },
    ];

    holidays.forEach(holiday => {
      it(`should trigger ${holiday.name} event`, () => {
        const isHoliday = (month: number, day: number) => {
          return month === holiday.month && day === holiday.day;
        };

        expect(isHoliday(holiday.month, holiday.day)).toBe(true);
      });
    });
  });

  describe('Negative events', () => {
    it('should have negative consequences', () => {
      const negativeEvent = {
        execute: (player: Player) => {
          player.c.money = (player.c.money ?? 0) - 500;
          player.c.happiness = (player.c.happiness ?? 100) - 20;
          return { moneyCost: 500, happinessCost: 20 };
        },
      };

      const result = negativeEvent.execute(testPlayer);

      expect(result.moneyCost).toBeGreaterThan(0);
      expect(result.happinessCost).toBeGreaterThan(0);
    });

    it('should be less frequent than positive events', () => {
      const negativeEventProbability = 0.1;
      const positiveEventProbability = 0.3;

      expect(negativeEventProbability).toBeLessThan(positiveEventProbability);
    });
  });

  describe('Conversation events', () => {
    it('should involve other characters', () => {
      const friend = new Person({
        id: 'friend-1',
        firstname: 'Friend',
        relationships: ['friend'],
      });

      testPlayer.r.push(friend);

      expect(testPlayer.r).toHaveLength(1);
    });

    it('should affect affinity', () => {
      const friend = new Person({
        id: 'friend-1',
        firstname: 'Friend',
        affinity: 50,
      });

      friend.affinity = (friend.affinity ?? 0) + 10;

      expect(friend.affinity).toBe(60);
    });
  });

  describe('Tutorial events', () => {
    it('should track tutorial progress', () => {
      const tutorialStep = 0;
      const totalSteps = 5;

      expect(tutorialStep).toBeLessThan(totalSteps);
    });

    it('should not repeat after completion', () => {
      const tutorialComplete = true;
      const shouldShowTutorial = !tutorialComplete;

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

  describe('Hobby habits', () => {
    it('adds a name-keyed habit without an id field', () => {
      new MeditationEvent().processAnswer(testPlayer, 0); // "Yes, meditate daily!"

      const meditation = (testPlayer.c.habits ?? []).find((h) => h.name === 'meditation');
      expect(meditation).toBeDefined();
      expect(meditation?.status).toBe('active');
      // Habit identity is `name`; no spurious `id` (guards the TS2353 regression).
      expect(meditation).not.toHaveProperty('id');
    });

    it('dedupes a repeated habit by name', () => {
      const event = new MeditationEvent();
      event.processAnswer(testPlayer, 0);
      event.processAnswer(testPlayer, 0);

      const meditationHabits = (testPlayer.c.habits ?? []).filter((h) => h.name === 'meditation');
      expect(meditationHabits).toHaveLength(1);
    });
  });
});
