package com.rokid.online.demo.android

import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager2.widget.ViewPager2
import com.rokid.online.speech.AsrClient
import com.rokid.online.speech.OnlineSpeechSdk
import com.rokid.online.speech.OnlineSpeechSdkConfig
import com.rokid.online.speech.TtsPlaybackState
import com.rokid.online.speech.TtsAudioConfig
import com.rokid.online.speech.open.AndroidPcmTtsStreamPlayer
import com.rokid.online.speech.open.OpenSdkAudioSource
import com.rokid.security.glass3.open.sdk.GlassSdk
import com.rokid.security.glass3.open.sdk.client.IServiceConnectionCallback
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator

class MainActivity : AppCompatActivity(), DemoHost {

    private lateinit var tvLog: TextView

    private var sdk: OnlineSpeechSdk? = null
    private var asr: AsrClient? = null
    private var tts: com.rokid.online.speech.TtsClient? = null
    private val openAudioSource = OpenSdkAudioSource()
    private val ttsPlayer = AndroidPcmTtsStreamPlayer()

    private var logCount = 0
    private val logLines = ArrayDeque<String>()
    private val maxLogLines = 200

    private var isBindingRequested = false
    private var isGlassBound = false
    private var isAsrConnected = false
    private var isTtsConnected = false
    private var isAsrMicRunning = false

    companion object {
        private const val TAG = "OnlineSpeechDemo"
        private const val DEFAULT_TTS_TEXT = "你好，我是在线语音测试。现在正在进行 TTS 默认流式播放验证。"
    }

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

        tvLog = findViewById(R.id.tvLog)
        setupPager()
        log("onCreate ${envSummary()}")

        bindOpenSdkService()
    }

    private fun setupPager() {
        val tabs = findViewById<TabLayout>(R.id.tabLayout)
        val pager = findViewById<ViewPager2>(R.id.viewPager)
        pager.adapter = DemoPagerAdapter(this)
        TabLayoutMediator(tabs, pager) { tab, pos ->
            tab.text = when (pos) {
                0 -> "初始化"
                1 -> "ASR"
                else -> "TTS"
            }
        }.attach()
    }

    private fun bindOpenSdkService() {
        if (isBindingRequested || isGlassBound) return
        isBindingRequested = true
        GlassSdk.bindSecurityService(this, object : IServiceConnectionCallback {
            override fun onServiceConnected() {
                isBindingRequested = false
                isGlassBound = true
                log("GlassSdk bound")
            }

            override fun onServiceDisconnected() {
                isBindingRequested = false
                isGlassBound = false
                log("GlassSdk disconnected")
            }

            override fun onBindingDied() {
                isBindingRequested = false
                isGlassBound = false
                log("GlassSdk binding died")
            }
        })
    }

    override fun envSummary(): String {
        return "domain=${BuildConfig.DEMO_DOMAIN} asrPath=${BuildConfig.DEMO_ASR_PATH} ttsPath=${BuildConfig.DEMO_TTS_PATH} ak=${mask(BuildConfig.DEMO_AK)} sk=${mask(BuildConfig.DEMO_SK)}"
    }

    override fun initSdk(uid: String, deviceId: String) {
        if (!GlassSdk.isReady() && !isGlassBound) {
            bindOpenSdkService()
            log("GlassSdk not ready, bind requested")
        }
        val domain = BuildConfig.DEMO_DOMAIN
        val ak = BuildConfig.DEMO_AK
        val sk = BuildConfig.DEMO_SK
        if (domain.isBlank() || ak.isBlank() || sk.isBlank() || uid.isBlank() || deviceId.isBlank()) {
            log("domain/ak/sk/uid/deviceId required")
            return
        }

        closeSpeechClients(keepSdk = false)

        val cfg = OnlineSpeechSdkConfig(
            domain = domain,
            ak = ak,
            sk = sk,
            uid = uid,
            deviceId = deviceId,
            asrPath = BuildConfig.DEMO_ASR_PATH,
            ttsPath = BuildConfig.DEMO_TTS_PATH,
            trustAllCerts = true,
            staticHttpHeaders = mapOf(
                "appCredential" to "userInfo",
                "messageId" to "android-demo-${System.currentTimeMillis()}",
            ),
            staticMessageHeaders = mapOf(
                "appCredential" to "userInfo",
                "messageId" to "android-demo-${System.currentTimeMillis()}",
            ),
        )
        sdk = OnlineSpeechSdk(cfg)
        asr = sdk!!.createAsrClient().attachAudioSource(openAudioSource).also { setupAsrCallbacks(it) }
        tts = sdk!!.createTtsClient()
            .attachStreamPlayer(ttsPlayer)
            .also { setupTtsCallbacks(it) }

        log("init sdk uid=$uid deviceId=$deviceId trustAll=true asrPath=${BuildConfig.DEMO_ASR_PATH} ttsPath=${BuildConfig.DEMO_TTS_PATH}")
        log("SDK init done, tokenTail=${sdk?.currentTokenTail()} (clients created)")
    }

    override fun closeAll() {
        closeSpeechClients(keepSdk = false)
        sdk?.close()
        sdk = null
        runCatching { GlassSdk.unbindSecurityService() }
        isBindingRequested = false
        isGlassBound = false
        log("closeAll done")
    }

    override fun asrConnect() {
        val client = asr ?: return log("ASR connect ignored: init SDK first")
        client.connect()
        log("ASR connect() called")
    }

    override fun asrStartMic() {
        if (asr == null) return log("ASR startAsrWithMic ignored: init SDK first")
        if (!isAsrConnected) return log("ASR startAsrWithMic ignored: connect() first")
        runCatching { asr?.startAsrWithMic() }
            .onSuccess {
                isAsrMicRunning = true
                log("ASR startAsrWithMic()")
            }
            .onFailure { log("ASR startAsrWithMic failed: ${it.message}") }
    }

    override fun asrStopMic() {
        if (asr == null) return log("ASR stopAsrWithMic ignored: init SDK first")
        if (!isAsrMicRunning) return log("ASR stopAsrWithMic ignored: mic not running")
        runCatching { asr?.stopAsrWithMic() }
            .onSuccess {
                isAsrMicRunning = false
                log("ASR stopAsrWithMic()")
            }
            .onFailure { log("ASR stopAsrWithMic failed: ${it.message}") }
    }

    override fun asrClose() {
        if (asr == null) return log("ASR close ignored: init SDK first")
        if (isAsrMicRunning) {
            runCatching { asr?.stopAsrWithMic() }
            isAsrMicRunning = false
        }
        asr?.close()
        isAsrConnected = false
        log("ASR close()")
    }

    override fun ttsConnect() {
        if (tts == null) return log("TTS connect ignored: init SDK first")
        tts?.connect()
        log("TTS connect() called")
    }

    override fun ttsSpeakDefault() {
        if (tts == null) return log("TTS speak(default) ignored: init SDK first")
        if (!isTtsConnected) return log("TTS speak(default) ignored: connect() first")
        tts?.speak(DEFAULT_TTS_TEXT, TtsAudioConfig())
        log("TTS speak(default)")
    }

    override fun ttsStop() {
        if (tts == null) return log("TTS stop ignored: init SDK first")
        tts?.stop()
        log("TTS stop()")
    }

    override fun ttsClose() {
        if (tts == null) return log("TTS close ignored: init SDK first")
        tts?.close()
        isTtsConnected = false
        log("TTS close()")
    }

    private fun setupAsrCallbacks(asrClient: AsrClient) {
        asrClient.setListener(object : AsrClient.Listener {
            override fun onOpen() {
                isAsrConnected = true
                log("ASR websocket open")
            }
            override fun onStart(taskId: String) = log("ASR started: $taskId")
            override fun onPartialResult(taskId: String, text: String) = log("ASR partial: $text")
            override fun onFinalResult(taskId: String, text: String) = log("ASR final: $text")
            override fun onFinished(taskId: String) = log("ASR ended: $taskId")
            override fun onError(code: Int, message: String) {
                log("ASR error code=$code msg=$message")
            }
            override fun onClosed(code: Int, reason: String) {
                isAsrConnected = false
                isAsrMicRunning = false
                log("ASR closed code=$code reason=$reason")
            }
        })
    }

    private fun setupTtsCallbacks(ttsClient: com.rokid.online.speech.TtsClient) {
        ttsClient.setListener(object : com.rokid.online.speech.TtsClient.Listener {
            override fun onOpen() {
                isTtsConnected = true
                log("TTS websocket open")
            }
            override fun onStart(taskId: String) = log("TTS started: $taskId")
            override fun onAudioChunk(taskId: String, audio: ByteArray) = log("TTS chunk: ${audio.size}")
            override fun onFinished(taskId: String) = log("TTS ended: $taskId")
            override fun onError(code: Int, message: String) = log("TTS error code=$code msg=$message")
            override fun onClosed(code: Int, reason: String) {
                isTtsConnected = false
                log("TTS closed code=$code reason=$reason")
            }
            override fun onPlaybackStateChanged(state: TtsPlaybackState) = log("TTS playback=$state")
        })
    }

    private fun closeSpeechClients(keepSdk: Boolean) {
        runCatching {
            if (isAsrMicRunning) {
                asr?.stopAsrWithMic()
            }
        }
        isAsrMicRunning = false
        asr?.close()
        tts?.close()
        asr = null
        tts = null
        isAsrConnected = false
        isTtsConnected = false
        if (!keepSdk) {
            log("speech clients released")
        }
    }

    private fun log(msg: String) {
        val line = "#${++logCount} ${System.currentTimeMillis()} $msg"
        Log.d(TAG, line)
        runOnUiThread {
            logLines.addFirst(line)
            while (logLines.size > maxLogLines) {
                logLines.removeLast()
            }
            tvLog.text = logLines.joinToString(separator = "\n")
        }
    }

    private fun mask(v: String): String {
        if (v.isBlank()) return "-"
        return if (v.length <= 8) "***" else "***${v.takeLast(8)}"
    }

    override fun onDestroy() {
        closeAll()
        super.onDestroy()
    }
}
