/**
 * Conversation Service Tests
 */
import { describe, it, expect, beforeEach } from 'vitest';
import {
  ConversationMessage,
  ConversationObj,
} from '../../../src/events/conversations/types.js';

// Mock relationship templates for testing
const RELATIONSHIP_TEMPLATES: Record<string, { tone: string; prompt: string; boundaries: string[] }> = {
  partner: { tone: 'romantic', prompt: 'romantic partner', boundaries: ['respectful', 'affectionate'] },
  girlfriend: { tone: 'romantic', prompt: 'girlfriend', boundaries: ['respectful', 'affectionate'] },
  boyfriend: { tone: 'romantic', prompt: 'boyfriend', boundaries: ['respectful', 'affectionate'] },
  spouse: { tone: 'romantic', prompt: 'spouse', boundaries: ['respectful', 'affectionate'] },
  friend: { tone: 'casual', prompt: 'friend', boundaries: ['friendly'] },
  boss: { tone: 'professional', prompt: 'boss', boundaries: ['respectful', 'formal'] },
  teacher: { tone: 'professional', prompt: 'teacher', boundaries: ['respectful', 'educational'] },
  mother: { tone: 'nurturing', prompt: 'mother', boundaries: ['loving', 'protective'] },
  father: { tone: 'nurturing', prompt: 'father', boundaries: ['loving', 'protective'] },
  sibling: { tone: 'familiar', prompt: 'sibling', boundaries: ['teasing', 'supportive'] },
  classmate: { tone: 'casual', prompt: 'classmate', boundaries: ['friendly'] },
  coworker: { tone: 'professional', prompt: 'coworker', boundaries: ['respectful'] },
  acquaintance: { tone: 'neutral', prompt: 'acquaintance', boundaries: ['polite'] },
  default: { tone: 'neutral', prompt: 'person', boundaries: ['polite'] },
};

// Mock relationship priority
const RELATIONSHIP_PRIORITY: Record<string, number> = {
  spouse: 100,
  girlfriend: 95,
  boyfriend: 95,
  partner: 90,
  mother: 80,
  father: 80,
  sibling: 70,
  friend: 60,
  boss: 50,
  teacher: 50,
  coworker: 40,
  classmate: 35,
  acquaintance: 20,
};

// Mock functions
function getRelationshipConfig(relationships: string[]): { tone: string; prompt: string; boundaries: string[] } {
  if (!relationships || relationships.length === 0) {
    return RELATIONSHIP_TEMPLATES['default'];
  }

  // Find highest priority relationship
  let highestPriority = -1;
  let selectedRelationship = 'default';

  for (const rel of relationships) {
    const priority = RELATIONSHIP_PRIORITY[rel] ?? 0;
    if (priority > highestPriority) {
      highestPriority = priority;
      selectedRelationship = rel;
    }
  }

  return RELATIONSHIP_TEMPLATES[selectedRelationship] ?? RELATIONSHIP_TEMPLATES['default'];
}

function initializeMessagingTraits(traits: string[]): Record<string, number> {
  const result: Record<string, number> = {
    formality: 50,
    warmth: 50,
    verbosity: 50,
    directness: 50,
  };

  if (traits.includes('outgoing') || traits.includes('talkative')) {
    result.verbosity = 70;
    result.warmth = 65;
  }
  if (traits.includes('introverted') || traits.includes('quiet')) {
    result.verbosity = 30;
    result.warmth = 40;
  }
  if (traits.includes('friendly')) {
    result.warmth = 75;
  }

  return result;
}

function getMessagingStylePrompt(firstname: string, traits: string[]): string {
  const traitStr = traits.length > 0 ? traits.join(', ') : 'neutral';
  return `${firstname} is ${traitStr}. Respond in their style.`;
}

function detectVerbosityLevel(message: string): 'short' | 'medium' | 'long' {
  const length = message.length;
  if (length < 20) return 'short';
  if (length < 100) return 'medium';
  return 'long';
}

function getVerbosityPromptHint(level: 'short' | 'medium' | 'long'): string {
  switch (level) {
    case 'short':
      return 'Keep responses brief and concise.';
    case 'medium':
      return 'Use moderate length responses.';
    case 'long':
      return 'Feel free to elaborate and provide detailed responses.';
  }
}

describe('Conversation Service', () => {
  describe('ConversationMessage', () => {
    it('should create a message with content', () => {
      const message = new ConversationMessage('Hello there!');

      expect(message.message).toBe('Hello there!');
      expect(message.id).toBeDefined();
      expect(message.datetime).toBeDefined();
    });

    it('should include sender information', () => {
      const message = new ConversationMessage('Hi!', {
        sender: 'character-123',
      });

      expect(message.sender).toBe('character-123');
    });

    it('should include sentiment', () => {
      const positive = new ConversationMessage('Great news!', { sentiment: 'positive' });
      const negative = new ConversationMessage('Bad news...', { sentiment: 'negative' });
      const neutral = new ConversationMessage('Just info', { sentiment: 'neutral' });

      expect(positive.sentiment).toBe('positive');
      expect(negative.sentiment).toBe('negative');
      expect(neutral.sentiment).toBe('neutral');
    });

    it('should include date and time', () => {
      const message = new ConversationMessage('Timed message', {
        date: '2024-06-15',
        time: '14:30',
      });

      expect(message.date).toBe('2024-06-15');
      expect(message.time).toBe('14:30');
    });

    it('should add answer options', () => {
      const message = new ConversationMessage('What do you think?');
      message.addAnswerOption('Yes');
      message.addAnswerOption('No');
      message.addAnswerOption('Maybe');

      expect(message.answerOptions).toHaveLength(3);
      expect(message.answerOptions).toContain('Yes');
      expect(message.answerOptions).toContain('No');
      expect(message.answerOptions).toContain('Maybe');
    });
  });

  describe('ConversationObj', () => {
    it('should create a conversation object', () => {
      const convo = new ConversationObj('char-1', 'chat');

      expect(convo.id).toBeDefined();
      expect(convo.type).toBe('conversationEvent');
      expect(convo.cType).toBe('chat');
      expect(convo.character).toBe('char-1');
    });

    it('should add messages to conversation', () => {
      const convo = new ConversationObj('char-1');

      convo.addMessage('Hello!', 'char-1');
      convo.addMessage('Hi there!', 'player');

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

    it('should not add empty messages', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('');

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

    it('should not add single character messages', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('a');

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

    it('should get conversation history', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('First message');
      convo.addMessage('Second message');

      const history = convo.getConversation();

      expect(history).toHaveLength(2);
    });

    it('should set answer options', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('Choose one');
      convo.setAnswerOptions(['Option A', 'Option B']);

      const options = convo.getAnswerOptions();

      expect(options).toEqual(['Option A', 'Option B']);
    });

    it('should add single answer option', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('Question');
      convo.addAnswerOption('First option');
      convo.addAnswerOption('Second option');

      const options = convo.getAnswerOptions();

      expect(options).toHaveLength(2);
    });

    it('should track unread status', () => {
      const convo = new ConversationObj('char-1');

      expect(convo.unread).toBe(true);

      convo.unread = false;

      expect(convo.unread).toBe(false);
    });

    it('should serialize to JSON', () => {
      const convo = new ConversationObj('char-1', 'chat');
      convo.addMessage('Test message');

      const json = convo.toJSON();

      expect(json.id).toBeDefined();
      expect(json.type).toBe('conversationEvent');
      expect(json.character).toBe('char-1');
      expect(json.cType).toBe('chat');
    });
  });

  describe('getRelationshipConfig', () => {
    it('should return config for partner relationship', () => {
      const config = getRelationshipConfig(['partner']);

      expect(config.tone).toBe('romantic');
      expect(config.prompt).toContain('partner');
    });

    it('should return config for friend relationship', () => {
      const config = getRelationshipConfig(['friend']);

      expect(config.tone).toBe('casual');
    });

    it('should return config for family relationships', () => {
      const config = getRelationshipConfig(['mother']);

      expect(config.tone).toBe('nurturing');
    });

    it('should return config for professional relationships', () => {
      const config = getRelationshipConfig(['boss']);

      expect(config.tone).toBe('professional');
    });

    it('should prioritize most intimate relationship', () => {
      const config = getRelationshipConfig(['friend', 'spouse']);

      // Spouse should take priority over friend
      expect(config.tone).toBe('romantic');
    });

    it('should return default for unknown relationship', () => {
      const config = getRelationshipConfig(['unknown_type']);

      expect(config).toBeDefined();
      expect(config.tone).toBe('neutral');
    });

    it('should return default for no relationships', () => {
      const config = getRelationshipConfig([]);

      expect(config).toBeDefined();
      expect(config.tone).toBe('neutral');
    });
  });

  describe('RELATIONSHIP_TEMPLATES', () => {
    it('should have all required relationship types', () => {
      const requiredTypes = [
        'partner', 'girlfriend', 'boyfriend', 'spouse',
        'friend', 'boss', 'teacher', 'mother', 'father',
        'sibling', 'classmate', 'coworker', 'acquaintance', 'default'
      ];

      requiredTypes.forEach(type => {
        expect(RELATIONSHIP_TEMPLATES[type]).toBeDefined();
        expect(RELATIONSHIP_TEMPLATES[type].tone).toBeDefined();
        expect(RELATIONSHIP_TEMPLATES[type].prompt).toBeDefined();
        expect(RELATIONSHIP_TEMPLATES[type].boundaries).toBeDefined();
      });
    });
  });

  describe('RELATIONSHIP_PRIORITY', () => {
    it('should have romantic relationships as highest priority', () => {
      expect(RELATIONSHIP_PRIORITY.spouse).toBeGreaterThan(RELATIONSHIP_PRIORITY.friend);
      expect(RELATIONSHIP_PRIORITY.girlfriend).toBeGreaterThan(RELATIONSHIP_PRIORITY.friend);
      expect(RELATIONSHIP_PRIORITY.boyfriend).toBeGreaterThan(RELATIONSHIP_PRIORITY.friend);
    });

    it('should have family relationships mid-priority', () => {
      expect(RELATIONSHIP_PRIORITY.mother).toBeGreaterThan(RELATIONSHIP_PRIORITY.friend);
      expect(RELATIONSHIP_PRIORITY.mother).toBeLessThan(RELATIONSHIP_PRIORITY.spouse);
    });

    it('should have professional relationships lower than family', () => {
      expect(RELATIONSHIP_PRIORITY.boss).toBeLessThan(RELATIONSHIP_PRIORITY.mother);
      expect(RELATIONSHIP_PRIORITY.teacher).toBeLessThan(RELATIONSHIP_PRIORITY.father);
    });

    it('should have acquaintance as lowest priority', () => {
      expect(RELATIONSHIP_PRIORITY.acquaintance).toBeLessThan(RELATIONSHIP_PRIORITY.friend);
      expect(RELATIONSHIP_PRIORITY.acquaintance).toBeLessThan(RELATIONSHIP_PRIORITY.classmate);
    });
  });

  describe('initializeMessagingTraits', () => {
    it('should initialize traits for a character', () => {
      const traits = initializeMessagingTraits(['outgoing', 'creative']);

      expect(traits).toBeDefined();
      expect(typeof traits).toBe('object');
    });

    it('should vary traits based on personality', () => {
      const outgoingTraits = initializeMessagingTraits(['outgoing', 'talkative']);
      const shyTraits = initializeMessagingTraits(['introverted', 'quiet']);

      expect(outgoingTraits).toBeDefined();
      expect(shyTraits).toBeDefined();
      expect(outgoingTraits.verbosity).toBeGreaterThan(shyTraits.verbosity);
    });
  });

  describe('getMessagingStylePrompt', () => {
    it('should generate style prompt for character', () => {
      const prompt = getMessagingStylePrompt('Alice', ['friendly']);

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

  describe('detectVerbosityLevel', () => {
    it('should detect short messages', () => {
      const level = detectVerbosityLevel('ok');

      expect(level).toBe('short');
    });

    it('should detect medium messages', () => {
      const level = detectVerbosityLevel('That sounds like a great idea, I think we should do it!');

      expect(level).toBe('medium');
    });

    it('should detect long messages', () => {
      const longMessage = 'This is a very long message that goes on and on. It contains multiple sentences and provides a lot of detail about various topics. The user is clearly interested in having an in-depth conversation about the subject matter at hand.';

      const level = detectVerbosityLevel(longMessage);

      expect(level).toBe('long');
    });

    it('should handle empty messages', () => {
      const level = detectVerbosityLevel('');

      expect(level).toBe('short');
    });
  });

  describe('getVerbosityPromptHint', () => {
    it('should return hint for short verbosity', () => {
      const hint = getVerbosityPromptHint('short');

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

    it('should return hint for medium verbosity', () => {
      const hint = getVerbosityPromptHint('medium');

      expect(typeof hint).toBe('string');
    });

    it('should return hint for long verbosity', () => {
      const hint = getVerbosityPromptHint('long');

      expect(typeof hint).toBe('string');
    });
  });

  describe('Conversation flow', () => {
    it('should support full conversation flow', () => {
      const characterId = 'alice-1';
      const convo = new ConversationObj(characterId, 'chat');

      // Character initiates
      convo.addMessage('Hey, how are you doing?', characterId);

      // Player responds
      convo.addMessage('I am doing great, thanks for asking!', 'player');

      // Character continues
      convo.addMessage('That is wonderful to hear!', characterId);

      expect(convo.conversation).toHaveLength(3);
      expect(convo.conversation[0].sender).toBe('alice-1');
      expect(convo.conversation[1].sender).toBe('player');
      expect(convo.conversation[2].sender).toBe('alice-1');
    });

    it('should track conversation with questions', () => {
      const convo = new ConversationObj('bob-1');

      convo.addMessage('Want to hang out today?', 'bob-1');
      convo.setAnswerOptions(['Sure!', 'Maybe later', 'Not today']);

      expect(convo.getAnswerOptions()).toHaveLength(3);
    });
  });

  describe('Sentiment tracking', () => {
    it('should track positive sentiment', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('That is amazing news!', 'char-1', { sentiment: 'positive' });

      expect(convo.conversation[0].sentiment).toBe('positive');
    });

    it('should track negative sentiment', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('I am so disappointed...', 'char-1', { sentiment: 'negative' });

      expect(convo.conversation[0].sentiment).toBe('negative');
    });

    it('should track neutral sentiment', () => {
      const convo = new ConversationObj('char-1');
      convo.addMessage('The weather is nice today.', 'char-1', { sentiment: 'neutral' });

      expect(convo.conversation[0].sentiment).toBe('neutral');
    });
  });

  describe('Conversation types', () => {
    const conversationTypes = ['chat', 'checkIn', 'askAboutDay', 'flatter', 'deepConversation'] as const;

    conversationTypes.forEach(cType => {
      it(`should support ${cType} conversation type`, () => {
        const convo = new ConversationObj('char-1', cType);

        expect(convo.cType).toBe(cType);
      });
    });
  });

  describe('Message properties', () => {
    it('should access message properties correctly', () => {
      const message = new ConversationMessage('Test message', {
        sender: 'char-1',
        sentiment: 'positive',
        date: '2024-01-01',
        time: '12:00',
      });

      expect(message.message).toBe('Test message');
      expect(message.sender).toBe('char-1');
      expect(message.sentiment).toBe('positive');
      expect(message.date).toBe('2024-01-01');
      expect(message.time).toBe('12:00');
    });
  });
});
