/**
 * Conversation Testing Utilities
 *
 * Provides reusable helpers for testing conversation functionality
 * without requiring AI responses.
 */

import { v4 as uuidv4 } from 'uuid';
import { Player, Person, PendingConversationEvent, PendingEventType } from '../../src/models/index.js';
import { ConversationObj, ConversationMessage } from '../../src/events/conversations/types.js';

/**
 * Create a test player with sensible defaults
 */
export function createTestPlayer(overrides: Partial<{
  userId: string;
  firstname: string;
  lastname: string;
  age: number;
  sex: string;
  energy: number;
  money: number;
  diamonds: number;
  social: number;
  happiness: number;
  date: string;
  time: string;
  hourOfDay: number;
  dayOfYear: number;
  gameSpeed: number;
  relationships: Person[];
}> = {}): Player {
  const characterId = `player-${uuidv4().slice(0, 8)}`;
  const character = new Person({
    id: characterId,
    firstname: overrides.firstname ?? 'TestPlayer',
    lastname: overrides.lastname ?? 'User',
    age: overrides.age ?? 25,
    sex: overrides.sex ?? 'male',
    energy: overrides.energy ?? 100,
    money: overrides.money ?? 1000,
    social: overrides.social ?? 50,
    happiness: overrides.happiness ?? 50,
  });

  const player = new Player({
    userId: overrides.userId ?? `test-user-${uuidv4().slice(0, 8)}`,
    character,
    status: 'playing',
    controller: 'active',
    date: overrides.date ?? '2024-06-15',
    time: overrides.time ?? '14:30',
    hourOfDay: overrides.hourOfDay ?? 14,
    minuteOfHour: 30,
    dayOfYear: overrides.dayOfYear ?? 166,
    dayOfWeek: 6,
    gameSpeed: overrides.gameSpeed ?? 1000,
    diamonds: overrides.diamonds ?? 100,
    money: overrides.money ?? 1000,
    r: overrides.relationships ?? [],
  });

  return player;
}

/**
 * Create a test character (NPC) with specific properties
 */
export function createTestCharacter(overrides: Partial<{
  id: string;
  firstname: string;
  lastname: string;
  age: number;
  sex: string;
  affinity: number;
  familiarity: number;
  relationships: string[];
  traits: string[];
  mood: string;
}> = {}): Person {
  return new Person({
    id: overrides.id ?? `char-${uuidv4().slice(0, 8)}`,
    firstname: overrides.firstname ?? 'TestNPC',
    lastname: overrides.lastname ?? 'Character',
    age: overrides.age ?? 24,
    sex: overrides.sex ?? 'female',
    affinity: overrides.affinity ?? 50,
    familiarity: overrides.familiarity ?? 25,
    relationships: overrides.relationships ?? ['friend'],
    traits: overrides.traits ?? ['friendly', 'outgoing'],
    mood: overrides.mood ?? 'Calm',
  });
}

/**
 * Create a test conversation with a character
 */
export function createTestConversation(
  character: Person,
  options: Partial<{
    id: string;
    cType: string;
    messages: Array<{ message: string; sender: string; sentiment?: 'positive' | 'negative' | 'neutral' }>;
    unread: boolean;
  }> = {}
): ConversationObj {
  const convo = new ConversationObj(character, options.cType ?? 'chat');

  if (options.id) {
    convo.id = options.id;
  }

  if (options.messages) {
    for (const msg of options.messages) {
      convo.addMessage(msg.message, msg.sender, { sentiment: msg.sentiment });
    }
  }

  if (options.unread !== undefined) {
    convo.unread = options.unread;
  }

  return convo;
}

/**
 * Create a mock AI tool call response structure
 */
export function mockToolCallResponse(
  toolName: string,
  args: Record<string, unknown>
): {
  tool_calls: Array<{
    id: string;
    type: 'function';
    function: { name: string; arguments: string };
  }>;
  content: string | null;
} {
  return {
    tool_calls: [{
      id: `call_${uuidv4().slice(0, 8)}`,
      type: 'function',
      function: {
        name: toolName,
        arguments: JSON.stringify(args),
      },
    }],
    content: null,
  };
}

/**
 * Create a mock JSON response (non-tool-calling mode)
 */
export function mockJsonResponse(
  message: string,
  sentiment: 'positive' | 'negative' | 'neutral' = 'neutral'
): { content: string } {
  return {
    content: JSON.stringify({ message, sentiment }),
  };
}

/**
 * Create a pending conversation event for testing
 */
export function createTestPendingEvent(
  type: PendingEventType,
  characterId: string,
  overrides: Partial<{
    id: string;
    characterName: string;
    data: Record<string, unknown>;
    expiresAt: string;
    triggersAt: number;
    priority: 'low' | 'medium' | 'high';
  }> = {}
): PendingConversationEvent {
  return {
    id: overrides.id ?? `event_${uuidv4().slice(0, 8)}`,
    type,
    characterId,
    characterName: overrides.characterName ?? 'TestNPC',
    data: overrides.data ?? {},
    createdAt: new Date().toISOString(),
    expiresAt: overrides.expiresAt,
    triggersAt: overrides.triggersAt,
    priority: overrides.priority ?? 'medium',
  };
}

/**
 * Assert that a conversation message has the required structure
 */
export function assertMessageStructure(
  msg: unknown,
  expectedFields: Partial<{
    hasId: boolean;
    hasMessage: boolean;
    hasSender: boolean;
    hasSentiment: boolean;
    hasDatetime: boolean;
  }> = {}
): void {
  const defaults = {
    hasId: true,
    hasMessage: true,
    hasSender: false,
    hasSentiment: false,
    hasDatetime: true,
  };
  const checks = { ...defaults, ...expectedFields };

  if (checks.hasId) {
    expect(msg).toHaveProperty('id');
    expect(typeof (msg as any).id).toBe('string');
  }
  if (checks.hasMessage) {
    expect(msg).toHaveProperty('message');
    expect(typeof (msg as any).message).toBe('string');
  }
  if (checks.hasSender) {
    expect(msg).toHaveProperty('sender');
  }
  if (checks.hasSentiment) {
    expect(msg).toHaveProperty('sentiment');
    expect(['positive', 'negative', 'neutral']).toContain((msg as any).sentiment);
  }
  if (checks.hasDatetime) {
    expect(msg).toHaveProperty('datetime');
  }
}

/**
 * Assert that a conversation object has the required structure
 */
export function assertConversationStructure(convo: unknown): void {
  expect(convo).toHaveProperty('id');
  expect(convo).toHaveProperty('type', 'conversationEvent');
  expect(convo).toHaveProperty('conversation');
  expect(Array.isArray((convo as any).conversation)).toBe(true);
}

/**
 * Assert that a pending event has the required structure
 */
export function assertPendingEventStructure(event: unknown): void {
  expect(event).toHaveProperty('id');
  expect(event).toHaveProperty('type');
  expect(event).toHaveProperty('characterId');
  expect(event).toHaveProperty('characterName');
  expect(event).toHaveProperty('data');
  expect(event).toHaveProperty('createdAt');
  expect(event).toHaveProperty('priority');
}

/**
 * Generate a sequence of test messages for a conversation
 */
export function generateTestMessages(
  count: number,
  playerId: string,
  characterId: string
): Array<{ message: string; sender: string; sentiment?: 'positive' | 'negative' | 'neutral' }> {
  const messages: Array<{ message: string; sender: string; sentiment?: 'positive' | 'negative' | 'neutral' }> = [];
  const sentiments: Array<'positive' | 'negative' | 'neutral'> = ['positive', 'negative', 'neutral'];

  for (let i = 0; i < count; i++) {
    const isPlayer = i % 2 === 0;
    messages.push({
      message: `Test message ${i + 1} from ${isPlayer ? 'player' : 'character'}`,
      sender: isPlayer ? playerId : characterId,
      sentiment: sentiments[i % 3],
    });
  }

  return messages;
}

/**
 * Wait for a specified number of milliseconds
 */
export function wait(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}
