package com.rugvedsarika.voicereader.data

import android.content.Context
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.util.Log
import java.io.File
import java.util.Locale

class TTSManager(private val context: Context) : TextToSpeech.OnInitListener {

    private var tts: TextToSpeech? = null
    private var isInitialized = false
    private var onInitSuccess: (() -> Unit)? = null
    private var onWordRange: ((start: Int, end: Int) -> Unit)? = null
    private var onSpeechDone: (() -> Unit)? = null
    private var lastWordIndex = 0
    private var currentText: String? = null
    private var isPaused = false




    init {
        tts = TextToSpeech(context, this)
    }

    private fun setupProgressListener() {
        tts?.setOnUtteranceProgressListener(object : android.speech.tts.UtteranceProgressListener() {
            override fun onStart(utteranceId: String?) {}
            override fun onDone(utteranceId: String?) {
                onSpeechDone?.invoke()
            }
            override fun onError(utteranceId: String?) {
                onSpeechDone?.invoke()
            }
            override fun onRangeStart(utteranceId: String?, start: Int, end: Int, frame: Int) {
                lastWordIndex = start
                onWordRange?.invoke(start, end)
            }

        })
    }

    override fun onInit(status: Int) {
        if (status == TextToSpeech.SUCCESS) {
            isInitialized = true
            val defaultLocale = Locale.getDefault()
            setLanguage(defaultLocale) // Use our enhanced selection logic
            setupProgressListener()
            onInitSuccess?.invoke()
        } else {
            Log.e("TTSManager", "Initialization failed")
        }
    }


    fun setOnInitListener(listener: () -> Unit) {
        onInitSuccess = listener
        if (isInitialized) listener()
    }

    fun setSpeechProgressListener(onWord: (Int, Int) -> Unit, onDone: () -> Unit) {
        onWordRange = onWord
        onSpeechDone = onDone
    }


    fun speak(text: String, speed: Float = 1.0f, pitch: Float = 1.0f) {
        if (!isInitialized) return
        currentText = text
        lastWordIndex = 0
        isPaused = false
        tts?.apply {
            setSpeechRate(speed)
            setPitch(pitch)
            this@apply.speak(text, TextToSpeech.QUEUE_FLUSH, null, "TTS_SPEAK_ID")
        }
    }

    fun pause() {
        if (isInitialized && !isPaused) {
            tts?.stop()
            isPaused = true
        }
    }

    fun resume(speed: Float = 1.0f, pitch: Float = 1.0f) {
        if (isInitialized && isPaused && currentText != null) {
            val remainingText = currentText!!.substring(lastWordIndex)
            isPaused = false
            tts?.apply {
                setSpeechRate(speed)
                setPitch(pitch)
                this@apply.speak(remainingText, TextToSpeech.QUEUE_FLUSH, null, "TTS_SPEAK_ID")
            }
        }
    }



    fun stop() {
        tts?.stop()
        currentText = null
        lastWordIndex = 0
        isPaused = false
    }


    fun synthesizeToFile(text: String, file: File, speed: Float = 1.0f, pitch: Float = 1.0f, onComplete: (Boolean) -> Unit) {
        if (!isInitialized) {
            onComplete(false)
            return
        }
        tts?.apply {
            setSpeechRate(speed)
            setPitch(pitch)
            val params = Bundle()
            params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "TTS_FILE_ID")
            
            val result = this@apply.synthesizeToFile(text, params, file, "TTS_FILE_ID")
            onComplete(result == TextToSpeech.SUCCESS)
        }

    }

    fun getAvailableLanguages(): List<Locale> {
        val supportedLanguages = mapOf(
            "en" to Locale.US,
            "hi" to Locale("hi", "IN"),
            "mr" to Locale("mr", "IN")
        )
        
        val available = mutableListOf<Locale>()
        supportedLanguages.forEach { (lang, preferredLocale) ->
            val res = tts?.isLanguageAvailable(preferredLocale)
            if (res != null && res >= TextToSpeech.LANG_AVAILABLE) {
                available.add(preferredLocale)
            } else {
                // Fallback to generic language if preferred locale not available
                val generic = Locale(lang)
                val genRes = tts?.isLanguageAvailable(generic)
                if (genRes != null && genRes >= TextToSpeech.LANG_AVAILABLE) {
                    available.add(generic)
                }
            }
        }
        
        return available.sortedBy { it.displayName }
    }

    fun setLanguage(locale: Locale): Int {
        // Force regional locale for Marathi and Hindi if they are generic
        val targetLocale = when (locale.language) {
            "mr" -> if (locale.country.isEmpty()) Locale("mr", "IN") else locale
            "hi" -> if (locale.country.isEmpty()) Locale("hi", "IN") else locale
            else -> locale
        }

        val result = tts?.setLanguage(targetLocale) ?: TextToSpeech.LANG_NOT_SUPPORTED
        
        if (result >= TextToSpeech.LANG_AVAILABLE) {
            try {
                val voices = tts?.voices
                if (voices != null) {
                    // Filter for voices of the selected language
                    val filteredVoices = voices.filter { it.locale.language == targetLocale.language }
                    
                    // Priority for selection:
                    // 1. Matching country (e.g. mr_IN)
                    // 2. High quality (Neural/Expert voices)
                    // 3. Network voices for better natural intonation (especially for Marathi)
                    // 4. Quality score from the engine
                    
                    val bestVoice = filteredVoices.sortedWith(compareByDescending<android.speech.tts.Voice> {
                        it.locale.country == targetLocale.country
                    }.thenByDescending {
                        // For Marathi/Hindi, neural network voices are vastly superior
                        isNeuralVoice(it)
                    }.thenByDescending {
                        it.quality
                    }).firstOrNull()
                    
                    bestVoice?.let {
                        tts?.voice = it
                        Log.d("TTSManager", "Selected best voice: ${it.name} Quality: ${it.quality} Region: ${it.locale}")
                    }
                }
            } catch (e: Exception) {
                Log.e("TTSManager", "Error selecting specific voice", e)
            }
        }

        if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
            Log.e("TTSManager", "Language not supported or data missing: ${targetLocale.displayName}")
        }
        return result
    }

    private fun isNeuralVoice(voice: android.speech.tts.Voice): Boolean {
        // Many high-quality voices contain "network", "neural", or specific high-quality IDs
        val name = voice.name.lowercase()
        return name.contains("network") || name.contains("neural") || 
               name.contains("-x-") || voice.isNetworkConnectionRequired
    }

    fun getCurrentEngineInfo(): String {
        return tts?.defaultEngine ?: "Unknown Engine"
    }

    fun isGoogleEngine(): Boolean {
        return getCurrentEngineInfo().contains("com.google.android.tts")
    }

    fun getVoice(): android.speech.tts.Voice? {
        return tts?.voice
    }



    fun release() {
        tts?.stop()
        tts?.shutdown()
    }
}
