/**
 * MessageEvent Model Tests
 */
import { describe, it, expect } from 'vitest';
import { MessageEvent, type MessageEventData, type EventCategory } from '../../src/models/MessageEvent';

describe('MessageEvent Model', () => {
  describe('constructor', () => {
    it('should create a MessageEvent with minimal data', () => {
      const event = new MessageEvent({
        id: 'event-1',
        message: 'Something happened!',
        type: 'messageEvent',
      });

      expect(event.id).toBe('event-1');
      expect(event.message).toBe('Something happened!');
      expect(event.type).toBe('messageEvent');
    });

    it('should create a MessageEvent with full data', () => {
      const data: MessageEventData = {
        id: 'event-2',
        message: 'You got a promotion!',
        type: 'messageEvent',
        date: '2024-06-15',
        hour: 10,
        category: 'career',
        energyCost: 0,
        moneyCost: 0,
        diamondCost: 0,
        moneyReward: 1000,
        energyReward: 0,
        diamondReward: 10,
        affinityChange: 5,
        claimed: false,
      };
      const event = new MessageEvent(data);

      expect(event.id).toBe('event-2');
      expect(event.message).toBe('You got a promotion!');
      expect(event.category).toBe('career');
      expect(event.moneyReward).toBe(1000);
      expect(event.diamondReward).toBe(10);
      expect(event.affinityChange).toBe(5);
      expect(event.claimed).toBe(false);
    });
  });

  describe('event categories', () => {
    const categories: EventCategory[] = [
      'career', 'social', 'achievement', 'education',
      'health', 'finance', 'random', 'neutral', 'negative'
    ];

    categories.forEach(category => {
      it(`should handle category: ${category}`, () => {
        const event = new MessageEvent({
          id: '1',
          message: 'Test',
          type: 'messageEvent',
          category,
        });
        expect(event.category).toBe(category);
      });
    });
  });

  describe('rewards and costs', () => {
    it('should track positive rewards', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'You found money!',
        type: 'messageEvent',
        moneyReward: 500,
        energyReward: 10,
        diamondReward: 5,
      });

      expect(event.moneyReward).toBe(500);
      expect(event.energyReward).toBe(10);
      expect(event.diamondReward).toBe(5);
    });

    it('should track costs', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'You spent money!',
        type: 'messageEvent',
        moneyCost: 200,
        energyCost: 20,
        diamondCost: 3,
      });

      expect(event.moneyCost).toBe(200);
      expect(event.energyCost).toBe(20);
      expect(event.diamondCost).toBe(3);
    });

    it('should track affinity changes', () => {
      const positiveEvent = new MessageEvent({
        id: '1',
        message: 'Friendship improved!',
        type: 'messageEvent',
        affinityChange: 10,
      });
      expect(positiveEvent.affinityChange).toBe(10);

      const negativeEvent = new MessageEvent({
        id: '2',
        message: 'Friendship damaged!',
        type: 'messageEvent',
        affinityChange: -15,
      });
      expect(negativeEvent.affinityChange).toBe(-15);
    });
  });

  describe('claimable system', () => {
    it('should track claimed status', () => {
      const unclaimedEvent = new MessageEvent({
        id: '1',
        message: 'Unclaimed event',
        type: 'messageEvent',
        claimed: false,
      });
      expect(unclaimedEvent.claimed).toBe(false);

      const claimedEvent = new MessageEvent({
        id: '2',
        message: 'Claimed event',
        type: 'messageEvent',
        claimed: true,
        claimedAt: new Date().toISOString(),
      });
      expect(claimedEvent.claimed).toBe(true);
      expect(claimedEvent.claimedAt).toBeDefined();
    });

    it('should identify claimable events', () => {
      const claimableEvent = new MessageEvent({
        id: '1',
        message: 'You earned a reward!',
        type: 'messageEvent',
        moneyReward: 100,
        claimed: false,
      });

      // Claimable if has positive rewards and not claimed
      expect(claimableEvent.moneyReward).toBeGreaterThan(0);
      expect(claimableEvent.claimed).toBe(false);
    });

    it('should identify non-claimable events', () => {
      const negativeEvent = new MessageEvent({
        id: '1',
        message: 'You lost money!',
        type: 'messageEvent',
        moneyCost: 50,
        claimed: false,
      });

      // Negative events are not claimable
      expect(negativeEvent.moneyCost).toBeGreaterThan(0);
    });
  });

  describe('character involvement', () => {
    it('should track involved characters', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'You had a conversation with friends!',
        type: 'messageEvent',
        characters: [
          { id: 'char-1', firstname: 'Alice' },
          { id: 'char-2', firstname: 'Bob' },
        ],
      });

      expect(event.characters).toHaveLength(2);
      expect(event.characters?.[0].firstname).toBe('Alice');
      expect(event.characters?.[1].firstname).toBe('Bob');
    });

    it('should handle events without characters', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Random event happened',
        type: 'messageEvent',
      });

      expect(event.characters).toBeUndefined();
    });
  });

  describe('timing', () => {
    it('should track event date and hour', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Morning event',
        type: 'messageEvent',
        date: '2024-06-15',
        hour: 8,
      });

      expect(event.date).toBe('2024-06-15');
      expect(event.hour).toBe(8);
    });

    it('should handle events at different times of day', () => {
      const morningEvent = new MessageEvent({
        id: '1', message: 'Morning', type: 'messageEvent', hour: 6
      });
      expect(morningEvent.hour).toBe(6);

      const afternoonEvent = new MessageEvent({
        id: '2', message: 'Afternoon', type: 'messageEvent', hour: 14
      });
      expect(afternoonEvent.hour).toBe(14);

      const nightEvent = new MessageEvent({
        id: '3', message: 'Night', type: 'messageEvent', hour: 22
      });
      expect(nightEvent.hour).toBe(22);
    });
  });

  describe('serialization', () => {
    it('should serialize to JSON correctly', () => {
      const event = new MessageEvent({
        id: 'serialize-test',
        message: 'Test event',
        type: 'messageEvent',
        category: 'random',
        moneyReward: 100,
        claimed: false,
      });

      const json = JSON.stringify(event);
      const parsed = JSON.parse(json);

      expect(parsed.id).toBe('serialize-test');
      expect(parsed.message).toBe('Test event');
      expect(parsed.category).toBe('random');
      expect(parsed.moneyReward).toBe(100);
      expect(parsed.claimed).toBe(false);
    });
  });

  describe('isNegative detection', () => {
    it('should identify negative events by costs', () => {
      const costEvent = new MessageEvent({
        id: '1',
        message: 'You paid a fine',
        type: 'messageEvent',
        moneyCost: 100,
      });
      expect(costEvent.moneyCost).toBeGreaterThan(0);
    });

    it('should identify negative events by negative affinity', () => {
      const affinityEvent = new MessageEvent({
        id: '1',
        message: 'Friendship damaged',
        type: 'messageEvent',
        affinityChange: -10,
      });
      expect(affinityEvent.affinityChange).toBeLessThan(0);
    });

    it('should identify negative category events', () => {
      const negativeEvent = new MessageEvent({
        id: '1',
        message: 'Bad thing happened',
        type: 'messageEvent',
        category: 'negative',
      });
      expect(negativeEvent.category).toBe('negative');
    });
  });

  describe('isClaimable detection', () => {
    it('should be claimable with money reward', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Money reward',
        type: 'messageEvent',
        moneyReward: 50,
        claimed: false,
      });
      expect(event.moneyReward).toBeGreaterThan(0);
      expect(event.claimed).toBe(false);
    });

    it('should be claimable with energy reward', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Energy boost',
        type: 'messageEvent',
        energyReward: 25,
        claimed: false,
      });
      expect(event.energyReward).toBeGreaterThan(0);
    });

    it('should be claimable with diamond reward', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Diamond bonus',
        type: 'messageEvent',
        diamondReward: 5,
        claimed: false,
      });
      expect(event.diamondReward).toBeGreaterThan(0);
    });

    it('should be claimable with positive affinity', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Friendship improved',
        type: 'messageEvent',
        affinityChange: 10,
        claimed: false,
      });
      expect(event.affinityChange).toBeGreaterThan(0);
    });

    it('should not be claimable if already claimed', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Already claimed',
        type: 'messageEvent',
        moneyReward: 100,
        claimed: true,
      });
      expect(event.claimed).toBe(true);
    });

    it('should not be claimable with only costs', () => {
      const event = new MessageEvent({
        id: '1',
        message: 'Only costs',
        type: 'messageEvent',
        moneyCost: 50,
        claimed: false,
      });
      expect(event.moneyReward ?? 0).toBe(0);
      expect(event.moneyCost).toBeGreaterThan(0);
    });
  });
});
