package com.example.jarvis_stts

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.speech.RecognizerIntent
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import android.speech.tts.Voice
import android.util.Log
import android.view.View
import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import okhttp3.*
import org.vosk.Model
import org.vosk.android.StorageService
import java.io.IOException
import java.util.Locale
import android.provider.Settings
import android.net.Uri

class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {

    private lateinit var tvStatus: TextView
    private lateinit var etUrl: EditText
    private lateinit var spinnerVoices: Spinner
    private lateinit var tts: TextToSpeech

    private val client = OkHttpClient()
    private var webSocket: WebSocket? = null

    private var availableVoices = mutableListOf<Voice>()
    private var voiceNames = mutableListOf<String>()

    private val speechRecognizerLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == RESULT_OK && result.data != null) {
            val spokenText = result.data!!.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.get(0) ?: ""
            tvStatus.text = "Ich: $spokenText"
            
            if (webSocket != null) {
                webSocket?.send(spokenText)
                Log.d("JARVIS", "Sende an Server: $spokenText")
            } else {
                tvStatus.text = "Fehler: Nicht verbunden!"
                // Falls nicht verbunden, Dienst wieder starten
                tellServiceTo(JarvisService.ACTION_RESUME)
            }
        } else {
            tellServiceTo(JarvisService.ACTION_RESUME)
        }
    }

    private fun checkOverlayPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Log.d("JARVIS", "Overlay-Berechtigung fehlt. Öffne Einstellungen...")
                val intent = Intent(
                    Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:$packageName")
                )
                // Wirft den Nutzer in die Einstellungen.
                // Nach der Rückkehr muss die App meist neu gestartet/fokussiert werden.
                startActivity(intent)
                Toast.makeText(this, "Bitte erlaube JARVIS-AI, über anderen Apps zu erscheinen", Toast.LENGTH_LONG).show()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvStatus = findViewById(R.id.tvStatus)
        etUrl = findViewById(R.id.etUrl)
        spinnerVoices = findViewById(R.id.spinnerVoices)
        val btnConnect = findViewById<Button>(R.id.btnConnect)
        val btnSpeak = findViewById<Button>(R.id.btnSpeak)
        val prefs = getSharedPreferences("JarvisPrefs", MODE_PRIVATE)

        tts = TextToSpeech(this, this)
        val savedUrl = prefs.getString("server_url", "")
        etUrl.setText(savedUrl)

        // AUTOMATISCH VERBINDEN, falls eine URL gespeichert ist
        if (!savedUrl.isNullOrEmpty()) {
            connectToServer(savedUrl)
        }

        etUrl.setText(prefs.getString("server_url", ""))

        btnConnect.setOnClickListener {
            val url = etUrl.text.toString()
            if (url.isNotEmpty()) {
                prefs.edit().putString("server_url", url).apply()
                connectToServer(url)
            }
        }

        btnSpeak.setOnClickListener {
            tellServiceTo(JarvisService.ACTION_PAUSE)
            startVoiceInput()
        }

        checkPermissionsAndInit()
        checkOverlayPermission() // <-- Hier aufrufen!
    }

    // Wird aufgerufen, wenn die App im Hintergrund war und vom Service geweckt wird
    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        if (intent?.getBooleanExtra("WAKE_WORD_TRIGGERED", false) == true) {
            Log.d("JARVIS", "MainActivity: Wake Word vom Service empfangen! Starte Google...")
            // Kleiner Delay, damit die Audio-Hardware Zeit zum Umschalten hat
            tvStatus.postDelayed({
                startVoiceInput()
            }, 500)
        }
    }

    private fun checkPermissionsAndInit() {
        val permissions = mutableListOf(Manifest.permission.RECORD_AUDIO)

        // Notification Permission für Android 13+
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            permissions.add(Manifest.permission.POST_NOTIFICATIONS)
        }

        val missingPermissions = permissions.filter {
            ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
        }

        if (missingPermissions.isNotEmpty()) {
            ActivityCompat.requestPermissions(this, missingPermissions.toTypedArray(), 1)
        } else {
            initVoskModel()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 1 && grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
            initVoskModel()
        }
    }

    private fun initVoskModel() {
        StorageService.unpack(this, "model-de", "model",
            { _: Model ->
                Log.d("JARVIS", "Modell bereit, starte Hintergrund-Dienst...")
                tellServiceTo(JarvisService.ACTION_START)
                tvStatus.text = "Service läuft im Hintergrund!"
            },
            { exception: IOException ->
                Log.e("JARVIS", "Vosk Entpack-Fehler: ${exception.message}")
                tvStatus.text = "Fehler: Modell nicht gefunden"
            }
        )
    }

    private fun tellServiceTo(action: String) {
        val intent = Intent(this, JarvisService::class.java).apply {
            this.action = action
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent)
        } else {
            startService(intent)
        }
    }

    private fun startVoiceInput() {
        val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
            putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
            putExtra(RecognizerIntent.EXTRA_LANGUAGE, "de-DE")
            putExtra(RecognizerIntent.EXTRA_PROMPT, "Ich höre dir zu...")
            putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, 2000L)
            putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS, 2000L)
        }
        try {
            speechRecognizerLauncher.launch(intent)
        } catch (e: Exception) {
            tellServiceTo(JarvisService.ACTION_RESUME)
        }
    }

    private fun connectToServer(url: String) {
        webSocket?.close(1000, "Neuverbindung")
        val request = Request.Builder().url(url).build()
        webSocket = client.newWebSocket(request, object : WebSocketListener() {
            override fun onOpen(webSocket: WebSocket, response: Response) {
                runOnUiThread { tvStatus.text = "Verbunden!" }
            }
            override fun onMessage(webSocket: WebSocket, text: String) {
                runOnUiThread { tvStatus.text = "JARVIS-AI: $text" }
                speakOut(text)
            }
            override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                runOnUiThread {
                    tvStatus.text = "Verbindungsfehler!"
                    tellServiceTo(JarvisService.ACTION_RESUME)
                }
            }
        })
    }

    override fun onInit(status: Int) {
        if (status == TextToSpeech.SUCCESS) {
            tts.language = Locale.GERMAN
            setupVoiceSpinner()

            tts.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
                override fun onStart(utteranceId: String?) {}
                override fun onDone(utteranceId: String?) {
                    if (utteranceId == "TTS_DONE") {
                        runOnUiThread {
                            // Wenn Jarvis fertig gesprochen hat, lauschen wir wieder!
                            tellServiceTo(JarvisService.ACTION_RESUME)
                        }
                    }
                }
                @Deprecated("Deprecated in Java")
                override fun onError(utteranceId: String?) {
                    runOnUiThread { tellServiceTo(JarvisService.ACTION_RESUME) }
                }
            })
        }
    }

    private fun setupVoiceSpinner() {
        availableVoices.clear()
        voiceNames.clear()
        for (v in tts.voices) {
            if (v.locale.language == "de") {
                availableVoices.add(v)
                voiceNames.add("${v.name} (${if(v.isNetworkConnectionRequired) "Online" else "Lokal"})")
            }
        }
        val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, voiceNames)
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
        spinnerVoices.adapter = adapter
        spinnerVoices.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(p0: AdapterView<*>?, p1: View?, pos: Int, p3: Long) {
                tts.voice = availableVoices[pos]
            }
            override fun onNothingSelected(p0: AdapterView<*>?) {}
        }
    }

    private fun speakOut(text: String) {
        tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, "TTS_DONE")
    }

    override fun onDestroy() {
        webSocket?.close(1000, "App Ende")
        tts.shutdown()
        // Wir lassen den Service ABSICHTLICH nicht stoppen, wenn die Activity zerstört wird!
        super.onDestroy()
    }
}