/**
 * ElevenLabs Voice Agent Service
 *
 * Handles communication with ElevenLabs API for conversation agent management
 * Provides methods for creating, updating, and managing voice agents
 */

import { logger } from '@/lib/logger';
import {
  CreateAgentRequest,
  UpdateAgentRequest,
  ElevenLabsAgent,
  VoiceAgentConfig,
  VoiceAgentStatus,
} from '@/lib/types/voice-agent.types';

export class ElevenLabsServiceError extends Error {
  constructor(
    message: string,
    public statusCode?: number,
    public details?: any
  ) {
    super(message);
    this.name = 'ElevenLabsServiceError';
  }
}

export class ElevenLabsService {
  private apiKey: string | null = null;
  private baseUrl = 'https://api.elevenlabs.io/v1';
  private defaultVoiceId: string = '';

  private getApiKey(): string {
    if (this.apiKey) return this.apiKey;

    const apiKey = process.env.ELEVENLABS_API_KEY;
    if (!apiKey) {
      throw new Error('ELEVENLABS_API_KEY environment variable is required');
    }
    this.apiKey = apiKey;
    this.defaultVoiceId = process.env.ELEVENLABS_DEFAULT_VOICE_ID || 'pNInz6obpgDQGcFmaJgB'; // Adam voice
    return this.apiKey;
  }

  constructor() {
    // Lazy initialization - don't validate at module load time
  }

  /**
   * Create a new conversation agent
   */
  async createConversationAgent(
    config: VoiceAgentConfig,
    userId: string,
    userName?: string
  ): Promise<ElevenLabsAgent> {
    try {
      const agentName = `Mawidi Agent - ${userName || userId}`;
      const prompt = this.buildPrompt(config);
      const firstMessage = this.buildFirstMessage(config);

      const requestBody: CreateAgentRequest = {
        name: agentName,
        prompt,
        first_message: firstMessage,
        language: config.language === 'both' ? 'en' : config.language,
        voice_id: this.defaultVoiceId,
        llm: 'gpt-4o',
        temperature: 0.7,
        max_tokens: 1000,
      };

      logger.info('Creating ElevenLabs conversation agent', {
        userId,
        agentName,
        language: config.language,
      });

      const response = await fetch(`${this.baseUrl}/convai/agents`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'xi-api-key': this.getApiKey(),
        },
        body: JSON.stringify(requestBody),
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        logger.error('Failed to create ElevenLabs agent', new Error(response.statusText), {
          status: response.status,
          errorData,
        });
        throw new ElevenLabsServiceError(
          `Failed to create agent: ${response.statusText}`,
          response.status,
          errorData
        );
      }

      const agent: ElevenLabsAgent = await response.json();

      logger.info('Successfully created ElevenLabs agent', {
        userId,
        agentId: agent.agent_id,
      });

      return agent;
    } catch (error) {
      if (error instanceof ElevenLabsServiceError) {
        throw error;
      }
      logger.error('Error creating ElevenLabs agent', error);
      throw new ElevenLabsServiceError('Failed to create conversation agent');
    }
  }

  /**
   * Update an existing agent configuration
   */
  async updateAgentConfig(
    agentId: string,
    config: Partial<VoiceAgentConfig>
  ): Promise<ElevenLabsAgent> {
    try {
      const updateData: UpdateAgentRequest = {};

      if (config.language) {
        updateData.language = config.language === 'both' ? 'en' : config.language;
      }

      if (config.greetingMessage || config.voiceTone || config.useKnowledgeBase) {
        // Build updated prompt
        const fullConfig: VoiceAgentConfig = {
          language: config.language || 'both',
          voiceTone: config.voiceTone || 'professional',
          useKnowledgeBase: config.useKnowledgeBase ?? true,
          greetingMessage: config.greetingMessage,
          businessHours: config.businessHours,
          escalationKeywords: config.escalationKeywords,
        };
        updateData.prompt = this.buildPrompt(fullConfig);
        updateData.first_message = this.buildFirstMessage(fullConfig);
      }

      logger.info('Updating ElevenLabs agent', { agentId });

      const response = await fetch(`${this.baseUrl}/convai/agents/${agentId}`, {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
          'xi-api-key': this.getApiKey(),
        },
        body: JSON.stringify(updateData),
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        logger.error('Failed to update ElevenLabs agent', new Error(response.statusText), {
          status: response.status,
          agentId,
          errorData,
        });
        throw new ElevenLabsServiceError(
          `Failed to update agent: ${response.statusText}`,
          response.status,
          errorData
        );
      }

      const agent: ElevenLabsAgent = await response.json();

      logger.info('Successfully updated ElevenLabs agent', { agentId });

      return agent;
    } catch (error) {
      if (error instanceof ElevenLabsServiceError) {
        throw error;
      }
      logger.error('Error updating ElevenLabs agent', error);
      throw new ElevenLabsServiceError('Failed to update agent configuration');
    }
  }

  /**
   * Delete an agent
   */
  async deleteAgent(agentId: string): Promise<void> {
    try {
      logger.info('Deleting ElevenLabs agent', { agentId });

      const response = await fetch(`${this.baseUrl}/convai/agents/${agentId}`, {
        method: 'DELETE',
        headers: {
          'xi-api-key': this.getApiKey(),
        },
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        logger.error('Failed to delete ElevenLabs agent', new Error(response.statusText), {
          status: response.status,
          agentId,
          errorData,
        });
        throw new ElevenLabsServiceError(
          `Failed to delete agent: ${response.statusText}`,
          response.status,
          errorData
        );
      }

      logger.info('Successfully deleted ElevenLabs agent', { agentId });
    } catch (error) {
      if (error instanceof ElevenLabsServiceError) {
        throw error;
      }
      logger.error('Error deleting ElevenLabs agent', error);
      throw new ElevenLabsServiceError('Failed to delete agent');
    }
  }

  /**
   * Get agent status and details
   */
  async getAgentStatus(agentId: string): Promise<VoiceAgentStatus> {
    try {
      logger.debug('Fetching ElevenLabs agent status', { agentId });

      const response = await fetch(`${this.baseUrl}/convai/agents/${agentId}`, {
        method: 'GET',
        headers: {
          'xi-api-key': this.getApiKey(),
        },
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        logger.error('Failed to fetch agent status', new Error(response.statusText), {
          status: response.status,
          agentId,
          errorData,
        });
        throw new ElevenLabsServiceError(
          `Failed to fetch agent status: ${response.statusText}`,
          response.status,
          errorData
        );
      }

      const agent: ElevenLabsAgent = await response.json();

      // Mock status data (ElevenLabs API may not provide detailed stats)
      const status: VoiceAgentStatus = {
        agent_id: agent.agent_id,
        is_active: true,
        total_calls: 0, // Would need to track separately
      };

      return status;
    } catch (error) {
      if (error instanceof ElevenLabsServiceError) {
        throw error;
      }
      logger.error('Error fetching agent status', error);
      throw new ElevenLabsServiceError('Failed to fetch agent status');
    }
  }

  /**
   * Build AI prompt based on configuration
   */
  private buildPrompt(config: VoiceAgentConfig): string {
    const toneDescriptions = {
      professional:
        'You are a professional medical appointment assistant. Speak clearly and formally.',
      friendly:
        'You are a warm and friendly medical appointment assistant. Be conversational but respectful.',
      custom:
        'You are a helpful medical appointment assistant. Adapt your tone to the conversation.',
    };

    const languageInstruction =
      config.language === 'both'
        ? 'You can communicate in both English and Arabic. Detect the language the user speaks and respond accordingly.'
        : config.language === 'ar'
        ? 'You must communicate in Arabic only (العربية).'
        : 'You must communicate in English only.';

    const businessHoursInfo = config.businessHours
      ? `\n\nBusiness hours (${config.businessHours.timezone}):\n${config.businessHours.schedule
          .filter((s) => s.isOpen)
          .map((s) => `${s.day}: ${s.open} - ${s.close}`)
          .join('\n')}`
      : '';

    const knowledgeBaseInfo = config.useKnowledgeBase
      ? '\n\nYou have access to a knowledge base about the clinic. Use it to answer questions about services, doctors, procedures, and policies.'
      : '';

    const escalationInfo =
      config.escalationKeywords && config.escalationKeywords.length > 0
        ? `\n\nIf the user mentions any of these keywords, escalate to a human: ${config.escalationKeywords.join(', ')}`
        : '';

    return `${toneDescriptions[config.voiceTone]}

${languageInstruction}

Your role:
- Help patients book, reschedule, or cancel appointments
- Answer questions about clinic services and procedures
- Provide information about doctors and their specializations
- Assist with general inquiries about the clinic
- Be empathetic and understanding with patients
- If you cannot help with something, politely offer to connect them with staff

${businessHoursInfo}${knowledgeBaseInfo}${escalationInfo}

Always confirm important details like appointment dates and times before finalizing bookings.
If booking requires payment or deposit, inform the user and provide next steps.`;
  }

  /**
   * Build first message based on configuration
   */
  private buildFirstMessage(config: VoiceAgentConfig): string {
    // Use custom greeting if provided
    if (config.greetingMessage) {
      if (config.language === 'ar' && config.greetingMessage.ar) {
        return config.greetingMessage.ar;
      }
      if (config.language === 'en' && config.greetingMessage.en) {
        return config.greetingMessage.en;
      }
      // If language is 'both', use English as default
      if (config.greetingMessage.en) {
        return config.greetingMessage.en;
      }
    }

    // Default greetings
    const defaultGreetings = {
      ar: 'مرحباً! أنا مساعد المواعيد في مويدي. كيف يمكنني مساعدتك اليوم؟',
      en: 'Hello! I am the Mawidi appointment assistant. How can I help you today?',
      both: 'Hello! I am the Mawidi appointment assistant. How can I help you today?',
    };

    return defaultGreetings[config.language];
  }
}

// Export singleton instance
export const elevenLabsService = new ElevenLabsService();
