package com.example.modifier.repo

import android.util.Log
import androidx.compose.runtime.mutableStateListOf
import com.example.modifier.baseTag
import com.example.modifier.enums.RcsConfigureState
import com.example.modifier.model.TaskExecutionResult
import com.example.modifier.model.TaskItem
import com.example.modifier.utils.ADB
import com.example.modifier.utils.ROOT_ACCESS
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import org.apache.commons.collections4.queue.CircularFifoQueue
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

class GmsgStateRepo private constructor() {
    val logs = MutableStateFlow("")
    val rcsConfigureState = MutableStateFlow(RcsConfigureState.CONFIGURED)
    val watchList = mutableStateListOf<TaskExecutionResult>()

    companion object {
        private const val TAG = "$baseTag/GoogleMessageStateRepository"
        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { GmsgStateRepo() }
    }

    suspend fun startLogging() {
        try {
            val logsCache = CircularFifoQueue<String>(128)
            val p = withContext(Dispatchers.IO) {
                if (!ROOT_ACCESS) {
                    ADB.createProcess(ADB.adbPath, "-s", "localhost:5555", "shell")
                } else {
                    Runtime.getRuntime().exec("su")
                }
            }
            p.outputStream.bufferedWriter().use { writer ->
                writer.write("logcat -c")
                writer.newLine()
                writer.flush()
                writer.write("logcat BugleRcsEngine:D BugleTachygram:D BugleDataModel:D *:S -v time")
                writer.newLine()
                writer.flush()
            }
            p.inputStream
                .bufferedReader()
                .useLines { lines ->
                    lines.forEach { line ->
                        if (line.contains("destState=CheckPreconditionsState")) {
                            rcsConfigureState.emit(RcsConfigureState.NOT_CONFIGURED)
                        } else if (line.contains("destState=ReadyState")) {
                            rcsConfigureState.emit(RcsConfigureState.READY)
                        } else if (line.contains("destState=WaitingForOtpState")) {
                            rcsConfigureState.emit(RcsConfigureState.WAITING_FOR_OTP)
                        } else if (line.contains("destState=VerifyOtpState")) {
                            rcsConfigureState.emit(RcsConfigureState.VERIFYING_OTP)
                        } else if (line.contains("destState=ConfiguredState")) {
                            rcsConfigureState.emit(RcsConfigureState.CONFIGURED)
                        } else if (line.contains("destState=WaitingForRcsDefaultOnState")) {
                            rcsConfigureState.emit(RcsConfigureState.WAITING_FOR_DEFAULT_ON)
                        } else if (line.contains("destState=WaitingForGoogleTosState")) {
                            rcsConfigureState.emit(RcsConfigureState.WAITING_FOR_TOS)
                        } else if (line.contains("destState=RetryState")) {
                            rcsConfigureState.emit(RcsConfigureState.RETRY)
                        } else if (line.contains("destState=ReplayRequestState")) {
                            rcsConfigureState.emit(RcsConfigureState.REPLAY_REQUEST)
                        }
                        if (line.contains("Sending message")
                            && (line.contains("content_type=\"text/plain") ||
                                    line.contains("content_type=\"application/vnd.google.rcs.encrypted\"") ||
                                    line.contains("content_type=\"application/vnd.gsma.rcs-ft-http+xml\""))
                        ) {
                            val match = Regex("message_id=\"(.*?)\"").find(line)
                            if (match != null) {
                                val messageId = match.groupValues[1]
                                Log.i(TAG, "Sending message: $messageId")
                                if (watchList.isNotEmpty()) {
                                    watchList.last().messageId = messageId
                                }
                            }
                        } else if (line.contains("Received RCS Delivery Report message")) {
                            val match = Regex("rcsMessage\\{id:(.*?)\\}").find(line)
                            if (match != null) {
                                val messageId = match.groupValues[1]
                                Log.i(TAG, "Received RCS Delivery Report message: $messageId")
                                watchList.find { it.messageId == messageId }?.let {
                                    it.delivery = 1
                                }
                            }
                        }
                        Regex("(?<time>\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3}) I/BugleRcsEngine\\(\\W*\\d+\\): (?<log>.*)")
                            .matchEntire(line)
                            ?.apply {
                                val time = groups["time"]?.value?.dropLast(4)
                                val log = groups["log"]?.value
                                    ?.replace(Regex("\\[\\w+-\\w+-\\w+-\\w+-\\w+]"), "")
                                    ?.replace(Regex("\\[CONTEXT.*]"), "")
                                    ?.trim()

                                if (time != null && log != null) {
                                    if (log.contains("destState=")) {
                                        logsCache.add(
                                            "$time\n${
                                                log.replace("transitionTo: destState=", "")
                                            }"
                                        )
                                        logs.emit(logsCache.joinToString("\n"))
                                    }
                                }
                            }
                    }
                }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    suspend fun waitForRcsState(
        states: Array<RcsConfigureState>,
        timeout: Duration
    ): RcsConfigureState? {
        if (states.contains(rcsConfigureState.value)) {
            return rcsConfigureState.value
        }
        var state: RcsConfigureState? = null
        try {
            withTimeoutOrNull<Boolean>(timeout) {
                rcsConfigureState.collect {
                    Log.i("waitForRcsStateCollect", "waitForRcsStateCollect")
                    if (states.contains(it)) {
                        state = it
                        this.cancel()
                    }
                }
            }
        } catch (_: Exception) {
        }
        return state
    }

    suspend fun updateRcsState(state: RcsConfigureState) {
        rcsConfigureState.emit(state)
    }

    fun addToWatchList(res: TaskExecutionResult) {
        watchList.add(res)
        CoroutineScope(Dispatchers.IO).launch {
            delay(60.seconds)
            watchList.remove(res)
        }
    }
}