"""
AI Call Routes - Voice-to-Voice Chat with AI
Handles speech-to-text, AI responses, and text-to-speech for real-time voice communication
"""

from flask import Blueprint, request, jsonify, send_from_directory
import os
import json
import logging
import uuid
import tempfile
import asyncio
from datetime import datetime
from functools import wraps
import g4f
import sys

# Add path to parent directory for imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Import existing TTS functionality
from audio_generation.tts_service import generate_audio, generate_teacher_summary, AudioFormat
from search_ai.db import log_ai_request

# Speech-to-text functionality
try:
    import speech_recognition as sr
    SPEECH_RECOGNITION_AVAILABLE = True
except ImportError:
    SPEECH_RECOGNITION_AVAILABLE = False
    logging.warning("speech_recognition not available. Install with: pip install SpeechRecognition")

# For handling audio file conversion
try:
    from pydub import AudioSegment
    PYDUB_AVAILABLE = True
except ImportError:
    PYDUB_AVAILABLE = False
    logging.warning("pydub not available. Install with: pip install pydub")

# Alternative STT using OpenAI Whisper (if available)
try:
    import whisper
    WHISPER_AVAILABLE = True
    # Load a lightweight model for faster processing
    whisper_model = whisper.load_model("base")
except ImportError:
    WHISPER_AVAILABLE = False
    logging.warning("whisper not available. Install with: pip install openai-whisper")

# Create Blueprint for AI call routes
ai_call_bp = Blueprint('ai_call', __name__, url_prefix='/api/ai_call')

# Setup logging
logger = logging.getLogger(__name__)

# Temporary audio storage
TEMP_AUDIO_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "temp_audio")
os.makedirs(TEMP_AUDIO_DIR, exist_ok=True)

# API key validation decorator
def validate_api_key(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        api_key = request.headers.get('api_key') or request.args.get('api_key') or request.form.get('api_key')
        
        try:
            from config import ALLOWED_KEYS
        except ImportError:
            ALLOWED_KEYS = ['test_key', 'demo_key']
        
        if api_key not in ALLOWED_KEYS:
            return jsonify({"status": "error", "message": "Invalid API key"}), 401
            
        return f(*args, **kwargs)
    return decorated_function

def convert_audio_to_wav(audio_file_path):
    """Convert audio file to WAV format for better compatibility"""
    if not PYDUB_AVAILABLE:
        logger.warning("pydub not available, returning original file")
        return audio_file_path  # Return as-is if pydub not available
    
    try:
        # Determine the input format from file extension
        file_ext = os.path.splitext(audio_file_path)[1].lower()
        
        # Try to load audio file with different methods based on format
        audio = None
        
        if file_ext in ['.webm', '.ogg']:
            # For WebM/OGG files, try different approaches
            try:
                # First try direct loading
                audio = AudioSegment.from_file(audio_file_path)
            except Exception as e1:
                logger.warning(f"Direct loading failed for {file_ext}: {e1}")
                try:
                    # Try with explicit format specification
                    if file_ext == '.webm':
                        audio = AudioSegment.from_file(audio_file_path, format="webm")
                    else:
                        audio = AudioSegment.from_file(audio_file_path, format="ogg")
                except Exception as e2:
                    logger.warning(f"Format-specific loading failed: {e2}")
                    # Try as raw audio
                    try:
                        audio = AudioSegment.from_file(audio_file_path, format="ogg", codec="opus")
                    except Exception as e3:
                        logger.error(f"All WebM/OGG loading methods failed: {e3}")
                        return audio_file_path
        
        elif file_ext in ['.mp4', '.m4a']:
            try:
                audio = AudioSegment.from_file(audio_file_path, format="mp4")
            except Exception as e:
                logger.warning(f"MP4 loading failed: {e}")
                try:
                    audio = AudioSegment.from_file(audio_file_path)
                except Exception as e2:
                    logger.error(f"MP4 fallback loading failed: {e2}")
                    return audio_file_path
        
        else:
            # For other formats (WAV, MP3, etc.)
            try:
                audio = AudioSegment.from_file(audio_file_path)
            except Exception as e:
                logger.error(f"General audio loading failed: {e}")
                return audio_file_path
        
        if audio is None:
            logger.error("Failed to load audio file")
            return audio_file_path
        
        # Convert to WAV with specific settings for better speech recognition
        # Use 16kHz sample rate and mono channel for optimal STT performance
        audio = audio.set_frame_rate(16000).set_channels(1).set_sample_width(2)
        
        # Create WAV filename
        wav_path = audio_file_path.rsplit('.', 1)[0] + '_converted.wav'
        
        # Export as WAV with PCM encoding
        audio.export(
            wav_path, 
            format="wav",
            parameters=["-acodec", "pcm_s16le"]  # Ensure PCM encoding
        )
        
        logger.info(f"Successfully converted {audio_file_path} to {wav_path}")
        return wav_path
        
    except Exception as e:
        logger.error(f"Error converting audio to WAV: {e}")
        # If conversion fails completely, return original file
        return audio_file_path

def speech_to_text_whisper(audio_file_path, language="en"):
    """Convert speech to text using OpenAI Whisper"""
    try:
        # Convert audio to proper format
        wav_path = convert_audio_to_wav(audio_file_path)
        
        # Check if file exists and has content
        if not os.path.exists(wav_path):
            return {
                "status": "error",
                "message": f"Audio file not found after conversion: {wav_path}"
            }
        
        file_size = os.path.getsize(wav_path)
        if file_size == 0:
            return {
                "status": "error",
                "message": "Audio file is empty after conversion"
            }
        
        logger.info(f"Processing audio with Whisper: {wav_path} ({file_size} bytes)")
        
        # Use Whisper for transcription with language hint
        result = whisper_model.transcribe(
            wav_path, 
            language=language if language != 'en' else None,  # Let Whisper auto-detect for English
            fp16=False,  # Disable FP16 for better compatibility
            verbose=False
        )
        
        # Clean up temporary WAV file if it was created during conversion
        if wav_path != audio_file_path and os.path.exists(wav_path):
            try:
                os.remove(wav_path)
            except Exception as e:
                logger.warning(f"Failed to clean up converted file {wav_path}: {e}")
        
        # Validate result
        if not result or 'text' not in result:
            return {
                "status": "error",
                "message": "Whisper returned invalid result"
            }
        
        transcribed_text = result["text"].strip()
        
        # Check if we got meaningful text
        if not transcribed_text:
            return {
                "status": "error",
                "message": "No speech detected in audio"
            }
        
        # Filter out common Whisper hallucinations
        hallucination_phrases = [
            "thank you for watching",
            "please subscribe",
            "you",
            ".",
            "..."
        ]
        
        if transcribed_text.lower() in hallucination_phrases:
            return {
                "status": "error",
                "message": "No clear speech detected (possible background noise)"
            }
        
        logger.info(f"Whisper transcription successful: '{transcribed_text[:100]}...'")
        
        return {
            "status": "success",
            "text": transcribed_text,
            "language": result.get("language", language),
            "confidence": 1.0  # Whisper doesn't provide confidence scores
        }
        
    except Exception as e:
        logger.error(f"Whisper STT error: {e}")
        
        # Provide more specific error messages
        error_msg = str(e)
        if "cuda" in error_msg.lower() or "memory" in error_msg.lower():
            error_msg = "GPU/Memory error during transcription"
        elif "ffmpeg" in error_msg.lower() or "codec" in error_msg.lower():
            error_msg = "Audio codec/format error - file may be corrupted"
        elif "model" in error_msg.lower():
            error_msg = "Whisper model error - please try again"
        else:
            error_msg = f"Speech recognition failed: {error_msg}"
        
        return {
            "status": "error",
            "message": error_msg
        }

def speech_to_text_google(audio_file_path, language="en-US"):
    """Convert speech to text using Google Speech Recognition"""
    try:
        r = sr.Recognizer()
        
        # Convert audio to proper format
        wav_path = convert_audio_to_wav(audio_file_path)
        
        # Check if file exists and has content
        if not os.path.exists(wav_path):
            return {
                "status": "error",
                "message": f"Audio file not found after conversion: {wav_path}"
            }
        
        file_size = os.path.getsize(wav_path)
        if file_size == 0:
            return {
                "status": "error",
                "message": "Audio file is empty after conversion"
            }
        
        logger.info(f"Processing audio with Google STT: {wav_path} ({file_size} bytes)")
        
        with sr.AudioFile(wav_path) as source:
            # Adjust for ambient noise with more aggressive settings
            r.adjust_for_ambient_noise(source, duration=0.5)
            
            # Record the audio data
            audio_data = r.record(source)
            
            # Check if we have audio data
            if not audio_data or len(audio_data.get_raw_data()) == 0:
                return {
                    "status": "error",
                    "message": "No audio data found in file"
                }
        
        # Recognize speech using Google with timeout
        logger.info(f"Sending {len(audio_data.get_raw_data())} bytes to Google STT")
        text = r.recognize_google(audio_data, language=language, timeout=10)
        
        # Clean up temporary WAV file if it was created during conversion
        if wav_path != audio_file_path and os.path.exists(wav_path):
            try:
                os.remove(wav_path)
            except Exception as e:
                logger.warning(f"Failed to clean up converted file {wav_path}: {e}")
        
        # Validate result
        if not text or not text.strip():
            return {
                "status": "error",
                "message": "No speech detected in audio"
            }
        
        logger.info(f"Google STT successful: '{text[:100]}...'")
        
        return {
            "status": "success",
            "text": text.strip(),
            "language": language,
            "confidence": 1.0  # Google doesn't provide confidence scores through this API
        }
        
    except sr.UnknownValueError:
        logger.warning("Google STT: Could not understand audio")
        return {
            "status": "error",
            "message": "Could not understand audio - please speak more clearly"
        }
    except sr.RequestError as e:
        logger.error(f"Google STT service error: {e}")
        error_msg = str(e)
        if "quota" in error_msg.lower() or "limit" in error_msg.lower():
            error_msg = "Speech recognition service quota exceeded"
        elif "network" in error_msg.lower() or "connection" in error_msg.lower():
            error_msg = "Network error connecting to speech recognition service"
        else:
            error_msg = f"Speech recognition service error: {error_msg}"
        
        return {
            "status": "error",
            "message": error_msg
        }
    except Exception as e:
        logger.error(f"Google STT error: {e}")
        
        # Provide more specific error messages
        error_msg = str(e)
        if "timeout" in error_msg.lower():
            error_msg = "Speech recognition timed out - audio may be too long"
        elif "format" in error_msg.lower() or "codec" in error_msg.lower():
            error_msg = "Audio format not supported by Google Speech Recognition"
        else:
            error_msg = f"Speech recognition failed: {error_msg}"
        
        return {
            "status": "error",
            "message": error_msg
        }

async def generate_ai_response(message, conversation_history=None, language="en-US"):
    """Generate AI response using G4F"""
    try:
        # Prepare conversation context
        context_messages = []
        
        # Add system message based on language
        system_prompts = {
            "en-US": "You are a helpful, friendly AI assistant having a natural voice conversation. Keep responses conversational, concise (2-3 sentences), and engaging. Speak as if you're talking to a friend.",
            "hi-IN": "आप एक सहायक, मित्रवत AI सहायक हैं जो प्राकृतिक आवाज़ में बातचीत कर रहे हैं। जवाब संवादात्मक, संक्षिप्त (2-3 वाक्य), और आकर्षक रखें। ऐसे बोलें जैसे आप किसी दोस्त से बात कर रहे हों।",
            "ne-NP": "तपाईं एक सहायक, मित्रवत AI सहायक हुनुहुन्छ जसले प्राकृतिक आवाजमा कुराकानी गर्दै हुनुहुन्छ। जवाफहरू संवादात्मक, संक्षिप्त (२-३ वाक्य), र आकर्षक राख्नुहोस्। मित्रसँग कुरा गरे जस्तै बोल्नुहोस्।",
            "es-ES": "Eres un asistente de IA útil y amigable que mantiene una conversación de voz natural. Mantén las respuestas conversacionales, concisas (2-3 oraciones) y atractivas. Habla como si estuvieras hablando con un amigo.",
            "fr-FR": "Vous êtes un assistant IA utile et amical qui maintient une conversation vocale naturelle. Gardez les réponses conversationnelles, concises (2-3 phrases) et engageantes. Parlez comme si vous parliez à un ami."
        }
        
        system_prompt = system_prompts.get(language, system_prompts["en-US"])
        context_messages.append({"role": "system", "content": system_prompt})
        
        # Add conversation history
        if conversation_history:
            for msg in conversation_history[-6:]:  # Last 6 messages for context
                role = "user" if msg.get("type") == "user" else "assistant"
                content = msg.get("content", "")
                if content:
                    context_messages.append({"role": role, "content": content})
        
        # Add current message
        context_messages.append({"role": "user", "content": message})
        
        # Log the AI request
        request_id = str(uuid.uuid4())
        log_ai_request(
            request_id=request_id,
            request_type="voice_chat",
            request_data={"message": message, "language": language},
            model="gpt-5-nano",
            tokens_used=len(message.split()) * 1.3
        )
        
        # Generate response using G4F
        try:
            response = await g4f.ChatCompletion.create_async(
                model="gpt-5-nano",
                provider="PollinationsAI",
                messages=context_messages,
                timeout=30
            )
            
            if response and len(response.strip()) > 0:
                ai_text = response.strip()
            else:
                # Fallback response
                ai_text = "I'm sorry, I didn't catch that. Could you please repeat?"
                
        except Exception as e:
            logger.error(f"G4F error: {e}")
            # Fallback responses based on language
            fallback_responses = {
                "en-US": "I'm having trouble processing right now. Could you try again?",
                "hi-IN": "मुझे अभी समझने में कठिनाई हो रही है। क्या आप फिर से कोशिश कर सकते हैं?",
                "ne-NP": "मलाई अहिले बुझ्न गाह्रो भइरहेको छ। के तपाईं फेरि प्रयास गर्न सक्नुहुन्छ?",
                "es-ES": "Estoy teniendo problemas para procesar ahora mismo. ¿Podrías intentarlo de nuevo?",
                "fr-FR": "J'ai des difficultés à traiter maintenant. Pourriez-vous réessayer?"
            }
            ai_text = fallback_responses.get(language, fallback_responses["en-US"])
        
        # Log the completion
        log_ai_request(
            request_id=request_id,
            response_data={"response": ai_text},
            completion_time=datetime.now(),
            status="success"
        )
        
        return ai_text
        
    except Exception as e:
        logger.error(f"Error generating AI response: {e}")
        return "I'm sorry, I'm having technical difficulties. Please try again."

@ai_call_bp.route('/speech-to-text', methods=['POST'])
@validate_api_key
def speech_to_text():
    """Convert speech audio to text"""
    try:
        # Check if audio file is provided
        if 'audio' not in request.files:
            return jsonify({
                "status": "error",
                "message": "No audio file provided"
            }), 400
        
        audio_file = request.files['audio']
        language = request.form.get('language', 'en-US')
        mime_type = request.form.get('mimeType', '')
        
        # Validate file
        if audio_file.filename == '':
            return jsonify({
                "status": "error",
                "message": "No file selected"
            }), 400
        
        # Generate unique filename with proper extension
        file_id = uuid.uuid4().hex
        original_ext = os.path.splitext(audio_file.filename)[1] or '.wav'
        temp_filename = f"temp_audio_{file_id}{original_ext}"
        temp_path = os.path.join(TEMP_AUDIO_DIR, temp_filename)
        
        # Save the uploaded file
        audio_file.save(temp_path)
        
        # Log file information for debugging
        file_size = os.path.getsize(temp_path)
        logger.info(f"Received audio file: {temp_filename}, size: {file_size} bytes, MIME: {mime_type}")
        
        # Convert language code for STT
        stt_language_map = {
            "en-US": "en",
            "hi-IN": "hi",
            "ne-NP": "ne",
            "es-ES": "es",
            "fr-FR": "fr"
        }
        stt_language = stt_language_map.get(language, "en")
        
        # Try different STT methods with improved error handling
        result = None
        conversion_attempted = False
        converted_path = None
        
        # First try Whisper if available (more accurate and handles more formats)
        if WHISPER_AVAILABLE:
            logger.info("Attempting Whisper STT")
            try:
                result = speech_to_text_whisper(temp_path, stt_language)
                
                # If Whisper fails with format error, try conversion
                if result.get("status") == "error" and "format" in result.get("message", "").lower():
                    logger.info("Whisper failed with format error, attempting conversion")
                    converted_path = convert_audio_to_wav(temp_path)
                    if converted_path != temp_path:
                        conversion_attempted = True
                        result = speech_to_text_whisper(converted_path, stt_language)
                        
            except Exception as e:
                logger.error(f"Whisper STT failed: {e}")
                result = None
        
        # Fallback to Google Speech Recognition if Whisper failed
        if not result or result.get("status") != "success":
            if SPEECH_RECOGNITION_AVAILABLE:
                logger.info("Attempting Google Speech Recognition")
                try:
                    # Try with converted file if available, otherwise original
                    file_to_use = converted_path if conversion_attempted and converted_path else temp_path
                    
                    # If we haven't tried conversion yet, try it now
                    if not conversion_attempted:
                        converted_path = convert_audio_to_wav(file_to_use)
                        if converted_path != file_to_use:
                            file_to_use = converted_path
                            conversion_attempted = True
                    
                    result = speech_to_text_google(file_to_use, language)
                    
                except Exception as e:
                    logger.error(f"Google STT failed: {e}")
                    result = {
                        "status": "error",
                        "message": f"Speech recognition failed: {str(e)}"
                    }
            else:
                result = {
                    "status": "error",
                    "message": "No speech recognition service available"
                }
        
        # If still no result, provide detailed error
        if not result or result.get("status") != "success":
            error_msg = "Speech recognition failed"
            if result and result.get("message"):
                error_msg = result["message"]
            
            # Provide more specific error messages
            if "format" in error_msg.lower() or "codec" in error_msg.lower():
                error_msg = f"Audio format not supported. Received: {mime_type}. Please try recording again or check your browser settings."
            elif "noise" in error_msg.lower() or "unclear" in error_msg.lower():
                error_msg = "Audio quality too poor or too much background noise. Please try recording in a quieter environment."
            elif "no speech" in error_msg.lower() or "silence" in error_msg.lower():
                error_msg = "No speech detected in the audio. Please speak more clearly and ensure your microphone is working."
                
            result = {
                "status": "error",
                "message": error_msg
            }
        
        # Clean up temporary files
        cleanup_files = [temp_path]
        if converted_path and converted_path != temp_path:
            cleanup_files.append(converted_path)
            
        for file_path in cleanup_files:
            try:
                if os.path.exists(file_path):
                    os.remove(file_path)
            except Exception as e:
                logger.warning(f"Failed to clean up file {file_path}: {e}")
        
        # Log result for debugging
        if result.get("status") == "success":
            logger.info(f"STT successful: '{result.get('text', '')[:50]}...'")
        else:
            logger.error(f"STT failed: {result.get('message')}")
        
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"Speech-to-text error: {e}")
        return jsonify({
            "status": "error",
            "message": f"Server error during speech recognition: {str(e)}"
        }), 500

@ai_call_bp.route('/chat', methods=['POST'])
@validate_api_key
def chat():
    """Process text message and return AI response with audio"""
    try:
        data = request.get_json()
        
        if not data or 'message' not in data:
            return jsonify({
                "status": "error",
                "message": "Missing message in request"
            }), 400
        
        message = data.get('message', '')
        conversation_history = data.get('conversation_history', [])
        language = data.get('language', 'en-US')
        voice = data.get('voice', 'sage')
        
        if not message.strip():
            return jsonify({
                "status": "error",
                "message": "Message cannot be empty"
            }), 400
        
        # Generate AI response
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        
        ai_response = loop.run_until_complete(
            generate_ai_response(message, conversation_history, language)
        )
        
        loop.close()
        
        # Generate audio for the response
        audio_result = generate_audio(
            text=ai_response,
            language_code=language,
            voice_id=voice,
            format=AudioFormat.WAV,
            speed=1.0,
            summarize=False  # Don't summarize for voice chat
        )
        
        if audio_result.get('status') == 'success':
            # Return response with audio URL
            return jsonify({
                "status": "success",
                "text": ai_response,
                "audio_url": audio_result.get('url'),
                "filename": audio_result.get('filename'),
                "duration": audio_result.get('duration', 0)
            })
        else:
            # Return text response even if audio generation fails
            return jsonify({
                "status": "success",
                "text": ai_response,
                "audio_error": audio_result.get('message', 'Audio generation failed')
            })
        
    except Exception as e:
        logger.error(f"Chat error: {e}")
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@ai_call_bp.route('/test-audio', methods=['GET'])
@validate_api_key
def test_audio():
    """Test audio generation for voice call"""
    try:
        language = request.args.get('language', 'en-US')
        voice = request.args.get('voice', 'sage')
        
        test_messages = {
            "en-US": "Hello! This is a test of the AI voice call system. Can you hear me clearly?",
            "hi-IN": "नमस्ते! यह AI आवाज़ कॉल सिस्टम का परीक्षण है। क्या आप मुझे स्पष्ट रूप से सुन सकते हैं?",
            "ne-NP": "नमस्कार! यो AI आवाज कल प्रणालीको परीक्षण हो। के तपाईंले मलाई स्पष्ट रूपमा सुन्न सक्नुहुन्छ?",
            "es-ES": "¡Hola! Esta es una prueba del sistema de llamadas de voz de IA. ¿Puedes oírme claramente?",
            "fr-FR": "Bonjour ! Ceci est un test du système d'appel vocal IA. Pouvez-vous m'entendre clairement ?"
        }
        
        test_message = test_messages.get(language, test_messages["en-US"])
        
        # Generate test audio
        audio_result = generate_audio(
            text=test_message,
            language_code=language,
            voice_id=voice,
            format=AudioFormat.WAV,
            speed=1.0,
            summarize=False
        )
        
        return jsonify({
            "status": "success",
            "message": test_message,
            "audio_result": audio_result
        })
        
    except Exception as e:
        logger.error(f"Test audio error: {e}")
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@ai_call_bp.route('/cleanup-temp', methods=['POST'])
@validate_api_key
def cleanup_temp():
    """Clean up temporary audio files"""
    try:
        deleted_count = 0
        
        for filename in os.listdir(TEMP_AUDIO_DIR):
            if filename.startswith('temp_audio_'):
                file_path = os.path.join(TEMP_AUDIO_DIR, filename)
                try:
                    os.remove(file_path)
                    deleted_count += 1
                except:
                    pass
        
        return jsonify({
            "status": "success",
            "deleted_files": deleted_count
        })
        
    except Exception as e:
        logger.error(f"Cleanup error: {e}")
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@ai_call_bp.route('/status', methods=['GET'])
@validate_api_key
def status():
    """Get AI call system status and capabilities"""
    return jsonify({
        "status": "success",
        "capabilities": {
            "speech_recognition": {
                "whisper": WHISPER_AVAILABLE,
                "google": SPEECH_RECOGNITION_AVAILABLE
            },
            "audio_processing": PYDUB_AVAILABLE,
            "tts": True,  # Always available through existing service
            "ai_chat": True  # G4F integration
        },
        "supported_languages": [
            {"code": "en-US", "name": "English (US)"},
            {"code": "hi-IN", "name": "Hindi"},
            {"code": "ne-NP", "name": "Nepali"},
            {"code": "es-ES", "name": "Spanish"},
            {"code": "fr-FR", "name": "French"}
        ],
        "available_voices": [
            {"id": "sage", "name": "Sage (Male)"},
            {"id": "nova", "name": "Nova (Female)"},
            {"id": "onyx", "name": "Onyx (Male)"},
            {"id": "shimmer", "name": "Shimmer (Female)"}
        ]
    }) 