/**
 * Voice Agent Unit Tests
 *
 * Tests for ElevenLabs voice agent service, validators, and utilities
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
  voiceAgentConfigSchema,
  provisionAgentSchema,
  updateAgentConfigSchema,
  callEventSchema,
} from '@/lib/validators/voice-agent.validator';
import { ElevenLabsService } from '@/lib/services/elevenlabs.service';

describe('Voice Agent Validators', () => {
  describe('voiceAgentConfigSchema', () => {
    it('should validate a valid configuration', () => {
      const validConfig = {
        language: 'en',
        voiceTone: 'professional',
        useKnowledgeBase: true,
      };

      const result = voiceAgentConfigSchema.parse(validConfig);
      expect(result).toEqual(validConfig);
    });

    it('should accept both language option', () => {
      const config = {
        language: 'both',
        voiceTone: 'friendly',
        useKnowledgeBase: false,
      };

      const result = voiceAgentConfigSchema.parse(config);
      expect(result.language).toBe('both');
    });

    it('should accept custom greeting messages', () => {
      const config = {
        language: 'both',
        voiceTone: 'professional',
        useKnowledgeBase: true,
        greetingMessage: {
          en: 'Hello from our clinic!',
          ar: 'مرحبا من عيادتنا!',
        },
      };

      const result = voiceAgentConfigSchema.parse(config);
      expect(result.greetingMessage?.en).toBe('Hello from our clinic!');
      expect(result.greetingMessage?.ar).toBe('مرحبا من عيادتنا!');
    });

    it('should validate business hours format', () => {
      const config = {
        language: 'en',
        voiceTone: 'professional',
        useKnowledgeBase: true,
        businessHours: {
          timezone: 'Asia/Dubai',
          schedule: [
            {
              day: 'monday',
              open: '09:00',
              close: '17:00',
              isOpen: true,
            },
          ],
        },
      };

      const result = voiceAgentConfigSchema.parse(config);
      expect(result.businessHours?.timezone).toBe('Asia/Dubai');
      expect(result.businessHours?.schedule).toHaveLength(1);
    });

    it('should reject invalid time format', () => {
      const config = {
        language: 'en',
        voiceTone: 'professional',
        useKnowledgeBase: true,
        businessHours: {
          timezone: 'Asia/Dubai',
          schedule: [
            {
              day: 'monday',
              open: '9am', // Invalid format
              close: '5pm', // Invalid format
              isOpen: true,
            },
          ],
        },
      };

      expect(() => voiceAgentConfigSchema.parse(config)).toThrow();
    });

    it('should accept escalation keywords', () => {
      const config = {
        language: 'en',
        voiceTone: 'professional',
        useKnowledgeBase: true,
        escalationKeywords: ['emergency', 'urgent', 'complaint'],
      };

      const result = voiceAgentConfigSchema.parse(config);
      expect(result.escalationKeywords).toHaveLength(3);
    });
  });

  describe('provisionAgentSchema', () => {
    it('should use default values when not provided', () => {
      const minimalConfig = {};

      const result = provisionAgentSchema.parse(minimalConfig);
      expect(result.language).toBe('both');
      expect(result.voiceTone).toBe('professional');
      expect(result.useKnowledgeBase).toBe(true);
    });

    it('should merge defaults with provided values', () => {
      const config = {
        language: 'ar',
        greetingMessage: {
          ar: 'مرحبا',
        },
      };

      const result = provisionAgentSchema.parse(config);
      expect(result.language).toBe('ar');
      expect(result.voiceTone).toBe('professional'); // default
      expect(result.useKnowledgeBase).toBe(true); // default
    });
  });

  describe('callEventSchema', () => {
    it('should validate a call started event', () => {
      const event = {
        call_id: 'call_123',
        agent_id: 'agent_456',
        status: 'started',
        start_time: '2025-12-03T10:00:00Z',
      };

      const result = callEventSchema.parse(event);
      expect(result.status).toBe('started');
      expect(result.call_id).toBe('call_123');
    });

    it('should validate a call ended event with transcript', () => {
      const event = {
        call_id: 'call_123',
        agent_id: 'agent_456',
        status: 'ended',
        start_time: '2025-12-03T10:00:00Z',
        end_time: '2025-12-03T10:05:00Z',
        duration_seconds: 300,
        transcript: [
          {
            speaker: 'agent',
            text: 'Hello, how can I help you?',
            timestamp: '2025-12-03T10:00:00Z',
          },
          {
            speaker: 'user',
            text: 'I need to book an appointment',
            timestamp: '2025-12-03T10:00:05Z',
          },
        ],
      };

      const result = callEventSchema.parse(event);
      expect(result.transcript).toHaveLength(2);
      expect(result.duration_seconds).toBe(300);
    });

    it('should reject invalid speaker type', () => {
      const event = {
        call_id: 'call_123',
        agent_id: 'agent_456',
        status: 'ended',
        transcript: [
          {
            speaker: 'bot', // Invalid
            text: 'Hello',
            timestamp: '2025-12-03T10:00:00Z',
          },
        ],
      };

      expect(() => callEventSchema.parse(event)).toThrow();
    });
  });
});

describe('ElevenLabsService', () => {
  let service: ElevenLabsService;
  let fetchMock: any;

  beforeEach(() => {
    // Mock environment variables
    process.env.ELEVENLABS_API_KEY = 'test-api-key';
    process.env.ELEVENLABS_DEFAULT_VOICE_ID = 'test-voice-id';

    // Create service instance
    service = new ElevenLabsService();

    // Mock fetch
    fetchMock = vi.fn();
    global.fetch = fetchMock;
  });

  describe('buildPrompt', () => {
    it('should build prompt for professional English agent', () => {
      const config = {
        language: 'en' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
      };

      const prompt = (service as any).buildPrompt(config);
      expect(prompt).toContain('professional medical appointment assistant');
      expect(prompt).toContain('English only');
      expect(prompt).toContain('knowledge base');
    });

    it('should build prompt for friendly Arabic agent', () => {
      const config = {
        language: 'ar' as const,
        voiceTone: 'friendly' as const,
        useKnowledgeBase: false,
      };

      const prompt = (service as any).buildPrompt(config);
      expect(prompt).toContain('warm and friendly');
      expect(prompt).toContain('Arabic only');
      expect(prompt).not.toContain('knowledge base');
    });

    it('should include business hours in prompt', () => {
      const config = {
        language: 'en' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
        businessHours: {
          timezone: 'Asia/Dubai',
          schedule: [
            { day: 'monday', open: '09:00', close: '17:00', isOpen: true },
            { day: 'tuesday', open: '09:00', close: '17:00', isOpen: true },
          ],
        },
      };

      const prompt = (service as any).buildPrompt(config);
      expect(prompt).toContain('Business hours');
      expect(prompt).toContain('Asia/Dubai');
      expect(prompt).toContain('monday: 09:00 - 17:00');
    });

    it('should include escalation keywords', () => {
      const config = {
        language: 'en' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
        escalationKeywords: ['emergency', 'urgent'],
      };

      const prompt = (service as any).buildPrompt(config);
      expect(prompt).toContain('escalate to a human');
      expect(prompt).toContain('emergency');
      expect(prompt).toContain('urgent');
    });
  });

  describe('buildFirstMessage', () => {
    it('should use custom greeting for English', () => {
      const config = {
        language: 'en' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
        greetingMessage: {
          en: 'Welcome to our clinic!',
        },
      };

      const message = (service as any).buildFirstMessage(config);
      expect(message).toBe('Welcome to our clinic!');
    });

    it('should use custom greeting for Arabic', () => {
      const config = {
        language: 'ar' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
        greetingMessage: {
          ar: 'مرحبا بكم في عيادتنا!',
        },
      };

      const message = (service as any).buildFirstMessage(config);
      expect(message).toBe('مرحبا بكم في عيادتنا!');
    });

    it('should use default greeting for English', () => {
      const config = {
        language: 'en' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
      };

      const message = (service as any).buildFirstMessage(config);
      expect(message).toContain('Mawidi');
      expect(message).toContain('appointment assistant');
    });

    it('should use default greeting for Arabic', () => {
      const config = {
        language: 'ar' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
      };

      const message = (service as any).buildFirstMessage(config);
      expect(message).toContain('مويدي');
      expect(message).toContain('مساعد');
    });
  });

  describe('createConversationAgent', () => {
    it('should create agent with correct API call', async () => {
      const mockAgent = {
        agent_id: 'agent_123',
        name: 'Test Agent',
        conversation_config: {},
      };

      fetchMock.mockResolvedValueOnce({
        ok: true,
        json: async () => mockAgent,
      });

      const config = {
        language: 'en' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
      };

      const result = await service.createConversationAgent(config, 'user_123', 'Test User');

      expect(fetchMock).toHaveBeenCalledWith(
        'https://api.elevenlabs.io/v1/convai/agents',
        expect.objectContaining({
          method: 'POST',
          headers: expect.objectContaining({
            'xi-api-key': 'test-api-key',
          }),
        })
      );

      expect(result.agent_id).toBe('agent_123');
    });

    it('should throw error on API failure', async () => {
      fetchMock.mockResolvedValueOnce({
        ok: false,
        status: 400,
        statusText: 'Bad Request',
        json: async () => ({ error: 'Invalid request' }),
      });

      const config = {
        language: 'en' as const,
        voiceTone: 'professional' as const,
        useKnowledgeBase: true,
      };

      await expect(
        service.createConversationAgent(config, 'user_123')
      ).rejects.toThrow('Failed to create agent');
    });
  });
});
