package top.yeij.cyrene.viewmodel

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import top.yeij.cyrene.domain.model.Message
import top.yeij.cyrene.domain.repository.ChatRepository
import top.yeij.cyrene.util.Constants
import top.yeij.cyrene.util.RecordState
import top.yeij.cyrene.util.VoiceRecorder
import top.yeij.cyrene.voice.tts.TextToSpeechEngine

private fun List<Message>.deduplicate(): List<Message> {
    if (isEmpty()) return this
    val seen = mutableSetOf<String>()
    return filter { seen.add(it.id) }
}

private fun List<Message>.removeWrappingDuplicates(): List<Message> {
    if (size < 3) return this
    val toRemove = mutableSetOf<String>()
    for (msg in this) {
        val containedCount = count { other ->
            other.id != msg.id &&
                other.content.isNotBlank() &&
                other.content.length < msg.content.length &&
                msg.content.contains(other.content) &&
                kotlin.math.abs(other.timestamp - msg.timestamp) < 2000
        }
        if (containedCount >= 2) {
            toRemove.add(msg.id)
        }
    }
    return if (toRemove.isEmpty()) this else filter { it.id !in toRemove }
}

enum class OverlayState {
    IDLE,
    LISTENING,
    PROCESSING,
    SPEAKING,
    WAITING,
}

class OverlayViewModel(
    private val chatRepository: ChatRepository,
    private val voiceRecorder: VoiceRecorder,
    private val ttsEngine: TextToSpeechEngine,
) : ViewModel() {

    private val _state = MutableStateFlow(OverlayState.WAITING)
    val state: StateFlow<OverlayState> = _state.asStateFlow()

    private val _messages = MutableStateFlow<List<Message>>(emptyList())
    val messages: StateFlow<List<Message>> = _messages.asStateFlow()

    private val _inputText = MutableStateFlow("")
    val inputText: StateFlow<String> = _inputText.asStateFlow()

    val voiceRecordState: StateFlow<RecordState> = voiceRecorder.state
    val voiceRecordDurationMs: StateFlow<Long> = voiceRecorder.durationMs

    // Animation ordering for message bubbles
    private var animCounter = 0
    private val _messageAnimIndex = MutableStateFlow<Map<String, Int>>(emptyMap())
    val messageAnimIndex: StateFlow<Map<String, Int>> = _messageAnimIndex.asStateFlow()

    private var silenceTimer: Job? = null
    private var processingTimeoutJob: Job? = null
    private var lastAssistantMessageId: String? = null

    init {
        viewModelScope.launch {
            chatRepository.observeMessages().collect { message ->
                _messages.update { list ->
                    val updated = list.toMutableList()
                    val existingIdx = updated.indexOfLast { it.id == message.id }
                    if (existingIdx >= 0) {
                        updated[existingIdx] = message
                    } else {
                        // Insert at correct position for ascending timestamp (oldest first for top-down layout)
                        val insertAt = updated.indexOfFirst { it.timestamp >= message.timestamp }
                        if (insertAt >= 0) updated.add(insertAt, message) else updated.add(message)
                        val animIdx = _messageAnimIndex.value.toMutableMap()
                        animIdx[message.id] = animCounter++
                        _messageAnimIndex.value = animIdx
                    }
                    updated.deduplicate()
                }

                // Any non-user response means the server acknowledged our message
                if (_state.value == OverlayState.PROCESSING && message.role != "user") {
                    cancelProcessingTimeout()
                    setWaiting()
                }

                if (message.role == "assistant" && !message.isStreaming && message.msgType == "chat") {
                    if (message.id != lastAssistantMessageId && message.content.isNotBlank()) {
                        lastAssistantMessageId = message.id
                        speakResponse(message.content)
                    }
                }
            }
        }
        viewModelScope.launch {
            chatRepository.isAssistantStreaming.collect { streaming ->
                if (streaming) {
                    cancelProcessingTimeout()
                } else if (_state.value == OverlayState.PROCESSING) {
                    delay(500)
                    if (_state.value == OverlayState.PROCESSING) {
                        setWaiting()
                    }
                }
            }
        }
        viewModelScope.launch {
            ttsEngine.onDone.collect {
                if (_state.value == OverlayState.SPEAKING) {
                    setWaiting()
                }
            }
        }
        viewModelScope.launch {
            chatRepository.messageClearEvents.collect {
                _messages.value = emptyList()
                _messageAnimIndex.value = emptyMap()
                animCounter = 0
            }
        }
        viewModelScope.launch {
            chatRepository.messageRemovals.collect { msgId ->
                _messages.update { list -> list.filter { it.id != msgId } }
                val idx = _messageAnimIndex.value.toMutableMap()
                idx.remove(msgId)
                _messageAnimIndex.value = idx
            }
        }
    }

    fun onInputChanged(text: String) {
        _inputText.value = text
    }

    fun sendText() {
        val text = _inputText.value.trim()
        if (text.isEmpty()) return
        _inputText.value = ""

        _state.value = OverlayState.PROCESSING
        cancelSilenceTimer()
        startProcessingTimeout()
        viewModelScope.launch {
            try {
                chatRepository.sendMessage(text, null)
            } catch (e: Exception) {
                Log.e("OverlayVM", "sendText failed: ${e.message}", e)
                if (_state.value == OverlayState.PROCESSING) setWaiting()
            }
        }
    }

    // --- Voice recording (WeChat-style gesture) ---

    fun startRecord() {
        voiceRecorder.start()
        _state.value = OverlayState.LISTENING
        cancelSilenceTimer()
    }

    fun lockRecord() {
        voiceRecorder.lock()
    }

    fun finishRecord() {
        val file = voiceRecorder.stop() ?: return
        val base64 = voiceRecorder.getBase64()
        voiceRecorder.deleteFile()
        if (base64.isNullOrBlank()) return

        _state.value = OverlayState.PROCESSING
        startProcessingTimeout()
        viewModelScope.launch {
            chatRepository.sendVoiceInput(base64, "voice_msg")
        }
    }

    fun cancelRecord() {
        voiceRecorder.cancel()
        setWaiting()
    }

    fun cancelCurrentAction() {
        if (voiceRecorder.state.value == RecordState.LOCKED) {
            voiceRecorder.cancel()
            setWaiting()
        }
    }

    private fun speakResponse(text: String) {
        if (text.isBlank()) return
        _state.value = OverlayState.SPEAKING
        ttsEngine.speak(text)
    }

    private fun setWaiting() {
        _state.value = OverlayState.WAITING
        startSilenceTimer()
    }

    fun stopSpeaking() {
        ttsEngine.stop()
        if (_state.value == OverlayState.SPEAKING) {
            setWaiting()
        }
    }

    fun sendScreenContext(content: String) {
        if (content.isBlank()) return
        viewModelScope.launch {
            chatRepository.sendScreenContext(content)
        }
    }

    fun finish() {
        _state.value = OverlayState.IDLE
        cancelSilenceTimer()
        voiceRecorder.cancel()
        ttsEngine.stop()
    }

    private fun startSilenceTimer() {
        cancelSilenceTimer()
        silenceTimer = viewModelScope.launch {
            delay(Constants.SILENCE_TIMEOUT_MS)
            if (_state.value == OverlayState.WAITING) {
                _state.value = OverlayState.IDLE
            }
        }
    }

    private fun cancelSilenceTimer() {
        silenceTimer?.cancel()
        silenceTimer = null
    }

    private fun startProcessingTimeout() {
        cancelProcessingTimeout()
        processingTimeoutJob = viewModelScope.launch {
            delay(15_000L)
            if (_state.value == OverlayState.PROCESSING) {
                Log.w("OverlayVM", "Processing timeout — no response in 15s, resetting to WAITING")
                setWaiting()
            }
        }
    }

    private fun cancelProcessingTimeout() {
        processingTimeoutJob?.cancel()
        processingTimeoutJob = null
    }

    override fun onCleared() {
        voiceRecorder.cancel()
        ttsEngine.shutdown()
        super.onCleared()
    }
}
