/**
 * Pending Conversation Events Tests
 *
 * Tests the pending event queue system that manages events
 * triggered by AI tool calls.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import {
  addPendingEvent,
  getNextPendingEvent,
  hasPendingEventFrom,
  getPendingEventCount,
} from '../../../src/events/conversations/pending_events.js';
import {
  createTestPlayer,
  createTestCharacter,
  createTestPendingEvent,
} from '../../utils/conversationTestUtils.js';
import type { Player, Person } from '../../../src/models/index.js';

describe('Pending Conversation Events', () => {
  let player: Player;
  let character: Person;

  beforeEach(() => {
    player = createTestPlayer();
    character = createTestCharacter({ id: 'char-1', firstname: 'Alice' });
    player.r = [character];
    player.pendingConversationEvents = [];
  });

  describe('addPendingEvent', () => {
    it('should add event to empty queue', () => {
      const event = createTestPendingEvent('activity_invite', character.id);

      addPendingEvent(player, event);

      expect(player.pendingConversationEvents).toHaveLength(1);
      expect(player.pendingConversationEvents[0].id).toBe(event.id);
    });

    it('should add multiple events from same character', () => {
      const event1 = createTestPendingEvent('activity_invite', character.id);
      const event2 = createTestPendingEvent('emotional_moment', character.id);

      addPendingEvent(player, event1);
      addPendingEvent(player, event2);

      expect(player.pendingConversationEvents).toHaveLength(2);
    });

    it('should replace duplicate event type from same character', () => {
      const event1 = createTestPendingEvent('activity_invite', character.id, {
        data: { activityType: 'coffee' },
      });
      const event2 = createTestPendingEvent('activity_invite', character.id, {
        data: { activityType: 'movie' },
      });

      addPendingEvent(player, event1);
      addPendingEvent(player, event2);

      // Should replace, not duplicate
      expect(player.pendingConversationEvents).toHaveLength(1);
      expect(player.pendingConversationEvents[0].data.activityType).toBe('movie');
    });

    it('should allow same event type from different characters', () => {
      const char2 = createTestCharacter({ id: 'char-2', firstname: 'Bob' });
      player.r.push(char2);

      const event1 = createTestPendingEvent('activity_invite', character.id);
      const event2 = createTestPendingEvent('activity_invite', char2.id);

      addPendingEvent(player, event1);
      addPendingEvent(player, event2);

      expect(player.pendingConversationEvents).toHaveLength(2);
    });

    it('should initialize array if undefined', () => {
      player.pendingConversationEvents = undefined as any;
      const event = createTestPendingEvent('activity_invite', character.id);

      addPendingEvent(player, event);

      expect(player.pendingConversationEvents).toBeDefined();
      expect(player.pendingConversationEvents).toHaveLength(1);
    });

    it('should process immediate events without queuing', () => {
      // Content safety: romantic relationships are adults only (18+). The
      // upgrade mechanic under test only applies to two adults.
      player.c.ageYears = 25;
      character.ageYears = 24;
      // Set character to dating relationship
      character.relationships = ['friend'];

      const event = createTestPendingEvent('confession_accepted', character.id, {
        data: {
          upgradesTo: 'dating',
          immediate: true,
        },
      });

      addPendingEvent(player, event);

      // Immediate events are processed, not queued
      expect(player.pendingConversationEvents).toHaveLength(0);
      // Character's relationship should be updated
      expect(character.relationships).toContain('dating');
    });

    it('should handle relationship_accepted immediate event', () => {
      // Content safety: romantic relationships are adults only (18+).
      player.c.ageYears = 25;
      character.ageYears = 24;
      character.relationships = ['dating'];

      const event = createTestPendingEvent('relationship_accepted', character.id, {
        data: {
          newRelationship: 'girlfriend',
          immediate: true,
        },
      });

      addPendingEvent(player, event);

      expect(player.pendingConversationEvents).toHaveLength(0);
      expect(character.relationships).toContain('girlfriend');
    });

    it('should handle breakup_initiated immediate event', () => {
      character.relationships = ['girlfriend'];

      const event = createTestPendingEvent('breakup_initiated', character.id, {
        data: {
          downgradesTo: 'ex',
          immediate: true,
        },
      });

      addPendingEvent(player, event);

      expect(player.pendingConversationEvents).toHaveLength(0);
      expect(character.relationships).toContain('ex');
      expect(character.relationships).not.toContain('girlfriend');
    });
  });

  describe('getNextPendingEvent', () => {
    it('should return null for empty queue', () => {
      const result = getNextPendingEvent(player);

      expect(result).toBeNull();
    });

    it('should return event from queue', () => {
      const event = createTestPendingEvent('activity_invite', character.id, {
        characterName: 'Alice',
        data: {
          activityType: 'coffee',
          urgency: 'soon',
          costs: { energy: 10, money: 10 },
        },
      });
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      expect(result).not.toBeNull();
      expect(result?.type).toBe('questionEvent');
    });

    it('should return null if character not found', () => {
      const event = createTestPendingEvent('activity_invite', 'unknown-char');
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      // Event should be removed because character doesn't exist
      expect(result).toBeNull();
      expect(player.pendingConversationEvents).toHaveLength(0);
    });

    it('should handle expired events', () => {
      const expiredEvent = createTestPendingEvent('activity_invite', character.id, {
        characterName: 'Alice',
        expiresAt: new Date(Date.now() - 86400000).toISOString(), // Expired yesterday
        data: {
          activityType: 'coffee',
          urgency: 'soon',
          costs: { energy: 10 },
        },
      });
      player.pendingConversationEvents.push(expiredEvent);

      const result = getNextPendingEvent(player);

      // Should return expiration message
      expect(result).not.toBeNull();
      expect(result?.type).toBe('messageEvent');
    });

    it('should prioritize high priority events', () => {
      const lowPriority = createTestPendingEvent('activity_invite', character.id, {
        characterName: 'Alice',
        priority: 'low',
        data: { activityType: 'walk', costs: { energy: 10 } },
      });
      const highPriority = createTestPendingEvent('date_request', character.id, {
        characterName: 'Alice',
        priority: 'high',
        data: { dateType: 'romantic', costs: { energy: 20, money: 50 } },
      });

      player.pendingConversationEvents.push(lowPriority);
      player.pendingConversationEvents.push(highPriority);

      const result = getNextPendingEvent(player);

      // High priority event should come first
      expect(result).not.toBeNull();
      // The result is a questionEvent generated from the high priority date_request
      expect((result as any).title).toContain('Date');
    });

    it('should not process scheduled events before trigger time', () => {
      const scheduledEvent = createTestPendingEvent('date_accepted', character.id, {
        characterName: 'Alice',
        triggersAt: (player.dayOfYear ?? 1) * 24 + (player.hourOfDay ?? 0) + 10, // 10 hours from now
        data: {
          dateType: 'romantic',
          costs: { energy: 20, money: 50 },
        },
      });
      player.pendingConversationEvents.push(scheduledEvent);

      const result = getNextPendingEvent(player);

      // Should not return the scheduled event yet
      expect(result).toBeNull();
    });

    it('should process scheduled events after trigger time', () => {
      const currentGameHour = (player.dayOfYear ?? 1) * 24 + (player.hourOfDay ?? 0);
      const scheduledEvent = createTestPendingEvent('date_accepted', character.id, {
        characterName: 'Alice',
        triggersAt: currentGameHour - 1, // 1 hour ago
        data: {
          dateType: 'romantic',
          suggestedTime: 'tonight',
          costs: { energy: 20, money: 50 },
        },
      });
      player.pendingConversationEvents.push(scheduledEvent);

      const result = getNextPendingEvent(player);

      expect(result).not.toBeNull();
      expect(result?.type).toBe('questionEvent');
    });
  });

  describe('hasPendingEventFrom', () => {
    it('should return false when no pending events', () => {
      const result = hasPendingEventFrom(player, character.id);

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

    it('should return true when character has pending event', () => {
      const event = createTestPendingEvent('activity_invite', character.id);
      player.pendingConversationEvents.push(event);

      const result = hasPendingEventFrom(player, character.id);

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

    it('should return false for different character', () => {
      const event = createTestPendingEvent('activity_invite', 'other-char');
      player.pendingConversationEvents.push(event);

      const result = hasPendingEventFrom(player, character.id);

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

  describe('getPendingEventCount', () => {
    it('should return 0 for empty queue', () => {
      const count = getPendingEventCount(player);

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

    it('should return correct count', () => {
      player.pendingConversationEvents.push(
        createTestPendingEvent('activity_invite', character.id),
        createTestPendingEvent('emotional_moment', character.id)
      );

      const count = getPendingEventCount(player);

      expect(count).toBe(2);
    });

    it('should return 0 when array is undefined', () => {
      player.pendingConversationEvents = undefined as any;

      const count = getPendingEventCount(player);

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

  describe('Event Type Handling', () => {
    it('should create activity_invite question event', () => {
      const event = createTestPendingEvent('activity_invite', character.id, {
        characterName: 'Alice',
        data: {
          activityType: 'coffee',
          urgency: 'soon',
          costs: { energy: 10, money: 10 },
        },
      });
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      expect(result?.type).toBe('questionEvent');
      expect((result as any).title).toBe('Activity Invitation');
      expect((result as any).answers.length).toBeGreaterThan(0);
    });

    it('should create date_request question event', () => {
      const event = createTestPendingEvent('date_request', character.id, {
        characterName: 'Alice',
        data: {
          dateType: 'romantic',
          costs: { energy: 25, money: 50 },
        },
      });
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      expect(result?.type).toBe('questionEvent');
      expect((result as any).title).toBe('Date Request');
    });

    it('should create confession question event', () => {
      const event = createTestPendingEvent('confession', character.id, {
        characterName: 'Alice',
        data: {
          confessionStyle: 'shy',
          hasBeenBuilding: true,
          canStartDating: true,
        },
      });
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      expect(result?.type).toBe('questionEvent');
      expect((result as any).title).toBe('Confession');
    });

    it('should create gift_received message event and apply happiness', () => {
      const initialHappiness = player.c.happiness ?? 50;
      const event = createTestPendingEvent('gift_received', character.id, {
        characterName: 'Alice',
        data: {
          giftType: 'flowers',
          description: 'a beautiful bouquet of flowers',
          happinessBoost: 10,
        },
      });
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      expect(result?.type).toBe('messageEvent');
      expect((result as any).title).toBe('Gift Received!');
      // Gift events apply happiness immediately
      expect(player.c.happiness).toBe(Math.min(100, initialHappiness + 10));
    });

    it('should handle emotional_moment question event', () => {
      player.c.social = 80; // High social stat for bonus options
      const event = createTestPendingEvent('emotional_moment', character.id, {
        characterName: 'Alice',
        data: {
          feelingType: 'love',
          intensity: 'intense',
          aboutPlayer: true,
          bonusOptions: true,
        },
      });
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      expect(result?.type).toBe('questionEvent');
      expect((result as any).title).toBe('Emotional Moment');
    });

    it('should handle favor_request question event', () => {
      const event = createTestPendingEvent('favor_request', character.id, {
        characterName: 'Alice',
        data: {
          favorType: 'advice',
          importance: 'medium',
          costs: { energy: 5 },
          affinityReward: 10,
        },
      });
      player.pendingConversationEvents.push(event);

      const result = getNextPendingEvent(player);

      expect(result?.type).toBe('questionEvent');
      expect((result as any).title).toBe('Favor Request');
    });
  });
});
