/**
 * Tool Availability Tests
 *
 * Tests which conversation tools are available based on:
 * - Relationship type (romantic, friendship, professional, etc.)
 * - Affinity level
 * - Tool cooldowns
 */

import { describe, it, expect, beforeEach } from 'vitest';
import {
  getAvailableTools,
  canUseTool,
  recordToolUse,
  toolMetadata,
} from '../../../src/events/conversations/tools.js';
import type { ToolCooldowns } from '../../../src/models/Player.js';

describe('Tool Availability', () => {
  describe('getAvailableTools - Affinity Requirements', () => {
    it('should always include send_message (no requirements)', () => {
      const tools = getAvailableTools([], 0);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('send_message');
    });

    it('should include send_message at zero affinity', () => {
      const tools = getAvailableTools(['acquaintance'], 0);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('send_message');
    });

    it('should not include suggest_activity at low affinity (< 30)', () => {
      const tools = getAvailableTools(['friend'], 25);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).not.toContain('suggest_activity');
    });

    it('should include suggest_activity at affinity >= 30', () => {
      const tools = getAvailableTools(['friend'], 30);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('suggest_activity');
    });

    it('should not include express_feeling at low affinity (< 40)', () => {
      const tools = getAvailableTools(['friend'], 35);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).not.toContain('express_feeling');
    });

    it('should include express_feeling at affinity >= 40', () => {
      const tools = getAvailableTools(['friend'], 40);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('express_feeling');
    });

    it('should not include give_gift at affinity < 45', () => {
      const tools = getAvailableTools(['friend'], 44);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).not.toContain('give_gift');
    });

    it('should include give_gift at affinity >= 45', () => {
      const tools = getAvailableTools(['friend'], 45);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('give_gift');
    });
  });

  describe('getAvailableTools - Relationship Requirements', () => {
    it('should not include ask_for_date for non-romantic relationships', () => {
      const tools = getAvailableTools(['friend'], 100);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).not.toContain('ask_for_date');
    });

    it('should include ask_for_date for dating relationships with high affinity', () => {
      const tools = getAvailableTools(['dating'], 50);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('ask_for_date');
    });

    it('should include ask_for_date for dating_match relationships', () => {
      const tools = getAvailableTools(['dating_match'], 50);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('ask_for_date');
    });

    it('should include ask_for_date for partner relationships', () => {
      const tools = getAvailableTools(['partner'], 50);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('ask_for_date');
    });

    it('should include ask_for_date for boyfriend/girlfriend relationships', () => {
      const boyfriendTools = getAvailableTools(['boyfriend'], 50);
      const girlfriendTools = getAvailableTools(['girlfriend'], 50);

      expect(boyfriendTools.map(t => (t as any).function.name)).toContain('ask_for_date');
      expect(girlfriendTools.map(t => (t as any).function.name)).toContain('ask_for_date');
    });

    it('should include ask_for_date for spouse relationships', () => {
      const tools = getAvailableTools(['spouse'], 50);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('ask_for_date');
    });

    it('should not include confess_feelings for dating relationships', () => {
      // Confess is for escalating to dating, not for those already dating
      const tools = getAvailableTools(['dating'], 100);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).not.toContain('confess_feelings');
    });

    it('should include confess_feelings for friend with high affinity', () => {
      const tools = getAvailableTools(['friend'], 65);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('confess_feelings');
    });

    it('should include confess_feelings for crush relationship', () => {
      const tools = getAvailableTools(['crush'], 65);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('confess_feelings');
    });

    it('should not include ask_to_be_official for friends', () => {
      const tools = getAvailableTools(['friend'], 100);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).not.toContain('ask_to_be_official');
    });

    it('should include ask_to_be_official for dating with high affinity', () => {
      const tools = getAvailableTools(['dating'], 70);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('ask_to_be_official');
    });
  });

  describe('getAvailableTools - Accept Tools', () => {
    it('should include accept_activity at low affinity (>= 20)', () => {
      const tools = getAvailableTools(['friend'], 20);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('accept_activity');
    });

    it('should include accept_date for romantic relationships', () => {
      const tools = getAvailableTools(['dating'], 40);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('accept_date');
    });

    it('should include accept_date for crush relationship', () => {
      const tools = getAvailableTools(['crush'], 40);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('accept_date');
    });

    it('should include accept_relationship for dating relationships', () => {
      const tools = getAvailableTools(['dating'], 50);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('accept_relationship');
    });

    it('should include accept_confession for friends with adequate affinity', () => {
      const tools = getAvailableTools(['friend'], 45);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('accept_confession');
    });
  });

  describe('getAvailableTools - Breakup Tools', () => {
    it('should include initiate_breakup for dating relationships', () => {
      const tools = getAvailableTools(['dating'], 10);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('initiate_breakup');
    });

    it('should include accept_breakup for partner relationships', () => {
      const tools = getAvailableTools(['partner'], 10);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('accept_breakup');
    });

    it('should not include breakup tools for non-romantic relationships', () => {
      const tools = getAvailableTools(['friend'], 10);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).not.toContain('initiate_breakup');
      expect(toolNames).not.toContain('accept_breakup');
    });
  });

  describe('getAvailableTools - Multiple Relationships', () => {
    it('should check all relationships for tool availability', () => {
      // Character has both friend and dating relationships
      const tools = getAvailableTools(['friend', 'dating'], 50);

      const toolNames = tools.map(t => (t as any).function.name);
      // Should have romantic tools because of dating relationship
      expect(toolNames).toContain('ask_for_date');
    });

    it('should allow tool if any relationship qualifies', () => {
      // Has crush relationship which allows accept_date
      const tools = getAvailableTools(['acquaintance', 'crush'], 40);

      const toolNames = tools.map(t => (t as any).function.name);
      expect(toolNames).toContain('accept_date');
    });
  });

  describe('canUseTool - Cooldowns', () => {
    let toolCooldowns: ToolCooldowns;

    beforeEach(() => {
      toolCooldowns = {};
    });

    it('should allow tool use when no cooldown history', () => {
      const result = canUseTool('suggest_activity', 'char-1', undefined, 100);

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

    it('should allow tool use when character has no cooldown history', () => {
      toolCooldowns['other-char'] = { suggest_activity: 50 };

      const result = canUseTool('suggest_activity', 'char-1', toolCooldowns, 100);

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

    it('should allow tool use when tool not in character cooldown history', () => {
      toolCooldowns['char-1'] = { express_feeling: 50 };

      const result = canUseTool('suggest_activity', 'char-1', toolCooldowns, 100);

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

    it('should block tool use during cooldown period', () => {
      toolCooldowns['char-1'] = { suggest_activity: 90 };

      // suggest_activity has 24-hour cooldown
      // At game hour 100, only 10 hours have passed
      const result = canUseTool('suggest_activity', 'char-1', toolCooldowns, 100);

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

    it('should allow tool use after cooldown expires', () => {
      toolCooldowns['char-1'] = { suggest_activity: 50 };

      // suggest_activity has 24-hour cooldown
      // At game hour 100, 50 hours have passed (>= 24)
      const result = canUseTool('suggest_activity', 'char-1', toolCooldowns, 100);

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

    it('should use correct cooldown for different tools', () => {
      toolCooldowns['char-1'] = {
        ask_for_date: 50,      // 72-hour cooldown
        express_feeling: 50,   // 48-hour cooldown
      };

      const currentGameHour = 100; // 50 hours since last use

      // express_feeling (48hr cooldown) should be allowed
      expect(canUseTool('express_feeling', 'char-1', toolCooldowns, currentGameHour)).toBe(true);

      // ask_for_date (72hr cooldown) should still be blocked
      expect(canUseTool('ask_for_date', 'char-1', toolCooldowns, currentGameHour)).toBe(false);
    });

    it('should allow tools with no cooldown defined', () => {
      // send_message has no cooldown
      const result = canUseTool('send_message', 'char-1', toolCooldowns, 100);

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

    it('should return false for unknown tools', () => {
      const result = canUseTool('unknown_tool', 'char-1', toolCooldowns, 100);

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

  describe('recordToolUse', () => {
    let toolCooldowns: ToolCooldowns;

    beforeEach(() => {
      toolCooldowns = {};
    });

    it('should record tool use for new character', () => {
      recordToolUse('suggest_activity', 'char-1', toolCooldowns, 100);

      expect(toolCooldowns['char-1']).toBeDefined();
      expect(toolCooldowns['char-1']['suggest_activity']).toBe(100);
    });

    it('should record tool use for existing character', () => {
      toolCooldowns['char-1'] = { express_feeling: 50 };

      recordToolUse('suggest_activity', 'char-1', toolCooldowns, 100);

      expect(toolCooldowns['char-1']['suggest_activity']).toBe(100);
      expect(toolCooldowns['char-1']['express_feeling']).toBe(50);
    });

    it('should update existing tool cooldown', () => {
      toolCooldowns['char-1'] = { suggest_activity: 50 };

      recordToolUse('suggest_activity', 'char-1', toolCooldowns, 100);

      expect(toolCooldowns['char-1']['suggest_activity']).toBe(100);
    });

    it('should track multiple characters independently', () => {
      recordToolUse('suggest_activity', 'char-1', toolCooldowns, 100);
      recordToolUse('suggest_activity', 'char-2', toolCooldowns, 105);

      expect(toolCooldowns['char-1']['suggest_activity']).toBe(100);
      expect(toolCooldowns['char-2']['suggest_activity']).toBe(105);
    });
  });

  describe('toolMetadata Consistency', () => {
    it('should have metadata for all tools', () => {
      const expectedTools = [
        'send_message',
        'suggest_activity',
        'express_feeling',
        'ask_for_date',
        'share_news',
        'request_favor',
        'give_gift',
        'confess_feelings',
        'ask_to_be_official',
        'accept_relationship',
        'accept_date',
        'accept_activity',
        'accept_confession',
        'initiate_breakup',
        'accept_breakup',
      ];

      for (const toolName of expectedTools) {
        expect(toolMetadata[toolName]).toBeDefined();
        expect(toolMetadata[toolName].name).toBe(toolName);
        expect(toolMetadata[toolName].canTriggerEvent).toBeDefined();
      }
    });

    it('should have category for all tools', () => {
      for (const [name, meta] of Object.entries(toolMetadata)) {
        expect(meta.category).toBeDefined();
        expect(['communication', 'social', 'romantic', 'emotional', 'informational']).toContain(meta.category);
      }
    });

    it('should have no cooldown for accept tools', () => {
      const acceptTools = ['accept_relationship', 'accept_date', 'accept_activity', 'accept_confession'];

      for (const toolName of acceptTools) {
        expect(toolMetadata[toolName].cooldownHours).toBe(0);
      }
    });
  });
});
