import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import OpenAI from 'openai';
import * as fs from 'fs';
import * as path from 'path';
import { nanoid } from 'nanoid';
import { VoiceFeedback } from '../../database/entities';
import { SaveVoiceFeedbackDto } from './dto/save-voice-feedback.dto';

export interface TranscriptionResult {
  text: string;
  language: string;
  duration?: number;
}

export interface ActivityExtractionResult {
  type: string; // 'feeding', 'sleep', 'diaper', 'medicine', 'milestone', 'activity'
  timestamp?: Date;
  details: Record<string, any>;
  confidence: number;
}

@Injectable()
export class VoiceService {
  private openai: OpenAI;
  private chatOpenAI: OpenAI;
  private logger = new Logger('VoiceService');

  // Supported languages for MVP
  private readonly SUPPORTED_LANGUAGES = ['en', 'es', 'fr', 'pt', 'zh'];

  constructor(
    private configService: ConfigService,
    @InjectRepository(VoiceFeedback)
    private readonly voiceFeedbackRepository: Repository<VoiceFeedback>,
  ) {
    // Check if Azure OpenAI is enabled
    const azureEnabled = this.configService.get<boolean>(
      'AZURE_OPENAI_ENABLED',
    );

    if (azureEnabled) {
      // Use Azure OpenAI for both Whisper and Chat
      const whisperEndpoint = this.configService.get<string>(
        'AZURE_OPENAI_WHISPER_ENDPOINT',
      );
      const whisperKey = this.configService.get<string>(
        'AZURE_OPENAI_WHISPER_API_KEY',
      );
      const chatEndpoint = this.configService.get<string>(
        'AZURE_OPENAI_CHAT_ENDPOINT',
      );
      const chatKey = this.configService.get<string>(
        'AZURE_OPENAI_CHAT_API_KEY',
      );

      if (whisperEndpoint && whisperKey) {
        this.openai = new OpenAI({
          apiKey: whisperKey,
          baseURL: `${whisperEndpoint}/openai/deployments/${this.configService.get<string>('AZURE_OPENAI_WHISPER_DEPLOYMENT')}`,
          defaultQuery: {
            'api-version': this.configService.get<string>(
              'AZURE_OPENAI_WHISPER_API_VERSION',
            ),
          },
          defaultHeaders: { 'api-key': whisperKey },
        });
        this.logger.log(
          'Azure OpenAI Whisper configured for voice transcription',
        );
      } else {
        this.logger.warn(
          'Azure OpenAI Whisper not fully configured. Voice transcription will be disabled.',
        );
      }

      if (chatEndpoint && chatKey) {
        this.chatOpenAI = new OpenAI({
          apiKey: chatKey,
          baseURL: `${chatEndpoint}/openai/deployments/${this.configService.get<string>('AZURE_OPENAI_CHAT_DEPLOYMENT')}`,
          defaultQuery: {
            'api-version': this.configService.get<string>(
              'AZURE_OPENAI_CHAT_API_VERSION',
            ),
          },
          defaultHeaders: { 'api-key': chatKey },
        });
        this.logger.log('Azure OpenAI Chat configured for activity extraction');
      } else {
        this.logger.warn(
          'Azure OpenAI Chat not configured. Using Whisper client for chat.',
        );
        this.chatOpenAI = this.openai;
      }
    } else {
      // Use standard OpenAI
      const apiKey = this.configService.get<string>('OPENAI_API_KEY');

      if (!apiKey || apiKey === 'sk-your-openai-api-key-here') {
        this.logger.warn(
          'OPENAI_API_KEY not configured. Voice features will be disabled.',
        );
      } else {
        this.openai = new OpenAI({
          apiKey,
        });
        this.chatOpenAI = this.openai;
        this.logger.log('OpenAI configured for voice transcription');
      }
    }
  }

  /**
   * Transcribe audio file to text using Whisper API
   */
  async transcribeAudio(
    audioBuffer: Buffer,
    language?: string,
  ): Promise<TranscriptionResult> {
    if (!this.openai) {
      throw new BadRequestException('Voice service not configured');
    }

    try {
      // Whisper API requires a file, so we need to create a temporary file
      const tempDir = path.join(process.cwd(), 'temp');
      if (!fs.existsSync(tempDir)) {
        fs.mkdirSync(tempDir, { recursive: true });
      }

      const tempFilePath = path.join(tempDir, `audio-${Date.now()}.wav`);
      fs.writeFileSync(tempFilePath, audioBuffer);

      const startTime = Date.now();

      const transcription = await this.openai.audio.transcriptions.create({
        file: fs.createReadStream(tempFilePath),
        model: 'whisper-1',
        language:
          language && this.SUPPORTED_LANGUAGES.includes(language)
            ? language
            : undefined, // Auto-detect if not specified
        response_format: 'verbose_json',
      });

      const duration = Date.now() - startTime;

      // Clean up temp file
      fs.unlinkSync(tempFilePath);

      this.logger.log(
        `Audio transcribed in ${duration}ms: "${transcription.text}" (${transcription.language})`,
      );

      return {
        text: transcription.text,
        language: transcription.language,
        duration,
      };
    } catch (error) {
      this.logger.error(`Transcription failed: ${error.message}`, error.stack);
      throw new BadRequestException('Failed to transcribe audio');
    }
  }

  /**
   * Extract activity information from transcribed text using GPT
   */
  async extractActivityFromText(
    text: string,
    language: string,
    childName?: string,
  ): Promise<ActivityExtractionResult> {
    if (!this.chatOpenAI) {
      throw new BadRequestException('Chat service not configured');
    }

    this.logger.log(`[Activity Extraction] Starting extraction for: "${text}"`);
    this.logger.log(
      `[Activity Extraction] Language: ${language}, Child: ${childName || 'none'}`,
    );

    try {
      const systemPrompt = `You are an intelligent assistant that interprets natural language commands related to baby care and extracts structured activity data.

Extract activity details from the user's text and respond ONLY with valid JSON (no markdown, no explanations).

**Supported Activity Types:**

1. **feeding** - Any mention of eating, drinking, nursing, bottle, breastfeeding, formula, solids, meals
   - Extract: amount (ml/oz), method (bottle/breast/solids), duration (minutes), side (left/right/both for breastfeeding), notes

2. **sleep** - Any mention of sleeping, napping, bedtime, waking up, dozed off
   - Extract: start_time, end_time, duration (minutes), quality (peaceful/restless/fussy), location (crib/bassinet/arms), notes

3. **diaper** - Any mention of diaper change, wet, dirty, bowel movement, pee, poop
   - Extract: type (wet/dirty/both), color, consistency, rash (true/false), notes

4. **medicine** - Any mention of medication, vitamin, supplement, drops, dose
   - Extract: name, dosage, unit, notes

5. **activity** - Any mention of play, exercise, walk, music, reading, tummy time, outdoor activities
   - Extract: activityType (play/walk/exercise/music/reading/tummy_time/outdoor/other), duration (minutes), description, notes

6. **milestone** - Any mention of first time events, developmental progress, achievements, new skills
   - Extract: description, category (motor/social/cognitive/language), notes

**Response Format:**
{
  "type": "feeding|sleep|diaper|medicine|activity|milestone|unknown",
  "timestamp": "ISO 8601 datetime if mentioned (e.g., '3pm', '30 minutes ago'), otherwise use current time",
  "details": {
    // For feeding:
    "feedingType": "bottle|breast|solids",
    "amount": number or null,
    "unit": "ml|oz" or null,
    "side": "left|right|both" or null (for breastfeeding only),
    "duration": number (minutes) or null,
    "notes": string or null

    // For sleep:
    "quality": "peaceful|restless|fussy",
    "location": "crib|bassinet|arms|bed|stroller|car seat",
    "duration": number (minutes) or null,
    "notes": string or null

    // For diaper:
    "diaperType": "wet|dirty|both",
    "color": string or null,
    "consistency": string or null,
    "rash": boolean or null,
    "notes": string or null

    // For medicine:
    "medicineName": string,
    "dosage": number or string,
    "unit": string or null,
    "route": "oral|topical|injection|other" or null,
    "reason": string or null,
    "notes": string or null

    // For activity:
    "activityType": "play|walk|exercise|music|reading|tummy_time|outdoor|other",
    "duration": number (minutes) or null,
    "description": string or null,
    "notes": string or null

    // For milestone:
    "description": string,
    "category": "motor|social|cognitive|language" or null,
    "notes": string or null
  },
  "confidence": 0.0-1.0,
  "action": "create_activity|unknown"
}

**Important:**
- Be smart about interpreting casual language (e.g., "fed him" = feeding activity)
- Infer reasonable defaults (e.g., if amount is mentioned without unit and sounds like bottles, assume "ml")
- Use "unknown" type only if the text is clearly NOT about baby care
- Set confidence based on how clearly the activity is described
- Assume current time for timestamp if no time is mentioned
- Be flexible with language variations and typos

If the text doesn't describe a trackable baby care activity:
{"type": "unknown", "details": {}, "confidence": 0, "action": "unknown"}`;

      const userPrompt = childName
        ? `Child name: ${childName}\nUser said: "${text}"`
        : `User said: "${text}"`;

      this.logger.log(
        `[Activity Extraction] Calling GPT-4o-mini with user prompt: ${userPrompt}`,
      );

      const startTime = Date.now();
      const completion = await this.chatOpenAI.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [
          { role: 'system', content: systemPrompt },
          { role: 'user', content: userPrompt },
        ],
        response_format: { type: 'json_object' },
      });
      const duration = Date.now() - startTime;

      this.logger.log(
        `[Activity Extraction] GPT response received in ${duration}ms`,
      );
      this.logger.log(
        `[Activity Extraction] Raw GPT response: ${completion.choices[0].message.content}`,
      );

      const result = JSON.parse(completion.choices[0].message.content);

      this.logger.log(
        `[Activity Extraction] Extracted activity: ${result.type} (confidence: ${result.confidence})`,
      );
      this.logger.log(
        `[Activity Extraction] Details: ${JSON.stringify(result.details || {})}`,
      );

      return {
        type: result.type,
        timestamp: result.timestamp ? new Date(result.timestamp) : null,
        details: result.details || {},
        confidence: result.confidence || 0,
      };
    } catch (error) {
      this.logger.error(
        `[Activity Extraction] Failed: ${error.message}`,
        error.stack,
      );
      throw new BadRequestException('Failed to extract activity from text');
    }
  }

  /**
   * Process voice input: transcribe + extract activity
   */
  async processVoiceInput(
    audioBuffer: Buffer,
    language?: string,
    childName?: string,
  ): Promise<{
    transcription: TranscriptionResult;
    activity: ActivityExtractionResult;
  }> {
    // Step 1: Transcribe audio
    const transcription = await this.transcribeAudio(audioBuffer, language);

    // Step 2: Extract activity from transcription
    const activity = await this.extractActivityFromText(
      transcription.text,
      transcription.language,
      childName,
    );

    return {
      transcription,
      activity,
    };
  }

  /**
   * Generate clarification question for ambiguous input
   */
  async generateClarificationQuestion(
    text: string,
    activityType: string,
    language: string,
  ): Promise<string> {
    if (!this.chatOpenAI) {
      throw new BadRequestException('Chat service not configured');
    }

    try {
      const systemPrompt = `You are a helpful assistant for tracking baby activities.
The user provided input about a "${activityType}" activity, but some information is missing or unclear.

Generate a brief, friendly clarification question in ${language} to help complete the activity log.
Respond ONLY with the question text, no formatting.`;

      const completion = await this.chatOpenAI.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [
          { role: 'system', content: systemPrompt },
          { role: 'user', content: `User said: "${text}"` },
        ],
        temperature: 0.7,
        max_tokens: 100,
      });

      return completion.choices[0].message.content.trim();
    } catch (error) {
      this.logger.error(
        `Clarification generation failed: ${error.message}`,
        error.stack,
      );
      // Fallback to generic question
      return 'Could you provide more details about this activity?';
    }
  }

  /**
   * Save user feedback on voice command accuracy
   */
  async saveFeedback(
    userId: string,
    feedbackDto: SaveVoiceFeedbackDto,
  ): Promise<VoiceFeedback> {
    try {
      const feedback = this.voiceFeedbackRepository.create({
        id: `vfb_${nanoid(16)}`,
        userId,
        childId: feedbackDto.childId,
        activityId: feedbackDto.activityId,
        transcript: feedbackDto.transcript,
        language: feedbackDto.language,
        extractedType: feedbackDto.extractedType,
        extractedData: feedbackDto.extractedData,
        confidence: feedbackDto.confidence,
        action: feedbackDto.action,
        finalType: feedbackDto.finalType,
        finalData: feedbackDto.finalData,
        userNotes: feedbackDto.userNotes,
      });

      const saved = await this.voiceFeedbackRepository.save(feedback);

      this.logger.log(
        `[Voice Feedback] User ${userId} ${feedbackDto.action} voice command for type: ${feedbackDto.extractedType}`,
      );

      return saved;
    } catch (error) {
      this.logger.error(
        `[Voice Feedback] Failed to save: ${error.message}`,
        error.stack,
      );
      throw new BadRequestException('Failed to save voice feedback');
    }
  }
}
