/**
 * Conversation Message Deduplication Tests
 *
 * Tests the duplicate detection logic in ConversationObj.addMessage()
 * to prevent the same message from appearing multiple times.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { ConversationObj } from '../../../src/events/conversations/types.js';
import {
  createTestCharacter,
  createTestPlayer,
  generateTestMessages,
} from '../../utils/conversationTestUtils.js';

describe('Conversation Message Deduplication', () => {
  let playerId: string;
  let character: ReturnType<typeof createTestCharacter>;

  beforeEach(() => {
    const player = createTestPlayer();
    playerId = player.c.id;
    character = createTestCharacter();
  });

  describe('Basic Deduplication', () => {
    it('should add the first instance of a message', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hello there!', playerId);

      expect(convo.conversation).toHaveLength(1);
      expect(convo.conversation[0].message).toBe('Hello there!');
    });

    it('should reject duplicate message with same sender', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hello there!', playerId);
      convo.addMessage('Hello there!', playerId);

      expect(convo.conversation).toHaveLength(1);
    });

    it('should reject duplicate message with same sender (character)', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hi!', character.id);
      convo.addMessage('Hi!', character.id);

      expect(convo.conversation).toHaveLength(1);
    });

    it('should allow same message from different senders', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hello!', playerId);
      convo.addMessage('Hello!', character.id);

      expect(convo.conversation).toHaveLength(2);
      expect(convo.conversation[0].sender).toBe(playerId);
      expect(convo.conversation[1].sender).toBe(character.id);
    });

    it('should allow different messages from same sender', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hello!', playerId);
      convo.addMessage('How are you?', playerId);

      expect(convo.conversation).toHaveLength(2);
    });
  });

  describe('Adjacent-message Deduplication', () => {
    it('allows repeating older messages when the last message is different', () => {
      const convo = new ConversationObj(character, 'chat');

      // Add 15 messages alternating between player and character
      const messages = generateTestMessages(15, playerId, character.id);
      for (const msg of messages) {
        convo.addMessage(msg.message, msg.sender);
      }

      // Try to add a duplicate of an older (non-last) message.
      const duplicateMsg = messages[10];
      convo.addMessage(duplicateMsg.message, duplicateMsg.sender);

      // Current implementation only deduplicates against the very last message.
      expect(convo.conversation).toHaveLength(16);
    });

    it('should allow duplicate message after window passes (21+ messages)', () => {
      const convo = new ConversationObj(character, 'chat');

      // Add initial message
      convo.addMessage('Unique message', playerId);

      // Add 20 more unique messages to push the first message out of window
      for (let i = 0; i < 20; i++) {
        const sender = i % 2 === 0 ? character.id : playerId;
        convo.addMessage(`Message number ${i + 2}`, sender);
      }

      expect(convo.conversation).toHaveLength(21);

      // Now add the same message again - should be allowed because
      // the original is outside the 20-message window
      convo.addMessage('Unique message', playerId);

      expect(convo.conversation).toHaveLength(22);
    });

    it('deduplicates only exact back-to-back duplicates', () => {
      const convo = new ConversationObj(character, 'chat');

      // Add 19 messages
      for (let i = 0; i < 19; i++) {
        const sender = i % 2 === 0 ? character.id : playerId;
        convo.addMessage(`Message ${i + 1}`, sender);
      }

      // Add a specific message at position 20
      convo.addMessage('The twentieth message', playerId);

      expect(convo.conversation).toHaveLength(20);

      // Try to duplicate the 20th message - should be rejected
      convo.addMessage('The twentieth message', playerId);
      expect(convo.conversation).toHaveLength(20);

      // Repeating a non-last message is allowed.
      convo.addMessage('Message 1', character.id);
      expect(convo.conversation).toHaveLength(21);
    });
  });

  describe('Edge Cases', () => {
    it('should not add empty messages', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('', playerId);

      expect(convo.conversation).toHaveLength(0);
    });

    it('should not add single character messages', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('a', playerId);

      expect(convo.conversation).toHaveLength(0);
    });

    it('should add two character messages', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('ab', playerId);

      expect(convo.conversation).toHaveLength(1);
    });

    it('should handle whitespace-only messages', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('   ', playerId);

      // The message has length > 1 but is just whitespace
      // Current implementation allows this (length check only)
      expect(convo.conversation).toHaveLength(1);
    });

    it('should handle messages with special characters', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hello! 😊', playerId);
      convo.addMessage('Hello! 😊', playerId); // Duplicate

      expect(convo.conversation).toHaveLength(1);
    });

    it('should treat different emoji as different messages', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hello! 😊', playerId);
      convo.addMessage('Hello! 😢', playerId);

      expect(convo.conversation).toHaveLength(2);
    });

    it('should handle very long messages', () => {
      const convo = new ConversationObj(character, 'chat');
      const longMessage = 'A'.repeat(5000);

      convo.addMessage(longMessage, playerId);
      convo.addMessage(longMessage, playerId); // Duplicate

      expect(convo.conversation).toHaveLength(1);
    });

    it('should handle undefined sender (defaults to character)', () => {
      const convo = new ConversationObj(character, 'chat');

      // When sender is undefined, it defaults to character.id
      convo.addMessage('Hello!', undefined);
      convo.addMessage('Hello!', undefined);

      expect(convo.conversation).toHaveLength(1);
    });
  });

  describe('Case Sensitivity', () => {
    it('should treat different case as different messages', () => {
      const convo = new ConversationObj(character, 'chat');

      convo.addMessage('Hello', playerId);
      convo.addMessage('HELLO', playerId);
      convo.addMessage('hello', playerId);

      // Case-sensitive comparison means all three are different
      expect(convo.conversation).toHaveLength(3);
    });
  });

  describe('Interleaved Conversations', () => {
    it('should handle rapid back-and-forth without duplicates', () => {
      const convo = new ConversationObj(character, 'chat');

      // Simulate a rapid conversation
      convo.addMessage("Hey!", playerId);
      convo.addMessage("Hi there!", character.id);
      convo.addMessage("How are you?", playerId);
      convo.addMessage("I'm good, you?", character.id);
      convo.addMessage("Great!", playerId);

      expect(convo.conversation).toHaveLength(5);
    });

    it('should handle repeated greetings in sequence', () => {
      const convo = new ConversationObj(character, 'chat');

      // Player says hi, character says hi back - both should be allowed
      convo.addMessage("Hi", playerId);
      convo.addMessage("Hi", character.id);

      expect(convo.conversation).toHaveLength(2);
    });
  });

  describe('fromData Restoration', () => {
    it('should apply deduplication when restoring from data', () => {
      // Create plain data with duplicates
      const data = {
        id: 'test-convo-1',
        cType: 'chat',
        character: character.id,
        conversation: [
          { message: 'Hello', sender: playerId },
          { message: 'Hello', sender: playerId }, // Duplicate
          { message: 'Hi there', sender: character.id },
        ],
        question: 0,
        unread: true,
      };

      const convo = ConversationObj.fromData(data, character);

      // fromData uses addMessage which applies deduplication
      expect(convo.conversation).toHaveLength(2);
    });
  });
});
