package com.example.vastlib.service

import android.util.Log
import com.example.vastlib.entity.action.Next
import com.example.vastlib.entity.action.PinAction
import com.example.vastlib.entity.report.ActionExec
import com.example.vastlib.entity.task.TaskConfig
import com.example.vastlib.pin.NotificationMessage
import com.example.vastlib.utils.JSONUtils
import com.example.vastlib.utils.LogUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext

class PinActionExecService(
    private val action: PinAction,
    override val taskConfig: TaskConfig
) : BaseActionExecService(taskConfig) {

    suspend fun executeAction(onFinish: (List<ActionExec>) -> Unit = {}) =
        withContext(Dispatchers.IO) {
            val actionExecList: MutableList<ActionExec> = mutableListOf()
            var currentStep = taskConfig.current_step
            kotlin.runCatching {
                val start = System.currentTimeMillis()
                var respCode = 200
                val messageLog: MutableList<NotificationMessage> = mutableListOf()
                val nextList = action.next
                while (isActive && (start + action.delay * 1000) > System.currentTimeMillis()) {
                    val notificationCache = taskConfig.notificationCache
                    if (nextList.isNotEmpty() && notificationCache.isNotEmpty()) {
                        val notificationMessages =
                            haveNextCatchTargetMessage(notificationCache, nextList)
                        if (notificationMessages.isNotEmpty()) {
                            LogUtils.info("catch target message...")
                            messageLog.addAll(notificationMessages)
                            break
                        }
                    }
                    LogUtils.info("waiting for target message... ${action.delay}")
                    delay(1000)
                }
                val cost = System.currentTimeMillis() - start
                if (messageLog.isNotEmpty()) {
                    val nextStep =
                        getNextStepIndex(nextList, JSONUtils.notificationToJsonString(messageLog), currentStep)
                    taskConfig.current_step = nextStep
                } else {
                    if (nextList.isNotEmpty()) {
                        taskConfig.current_step = Int.MAX_VALUE
                        respCode = ERROR_CODE_PIN_ACTION_TIMEOUT
                    } else {
                        taskConfig.current_step = ++currentStep
                        messageLog += taskConfig.notificationCache
                    }
                }
                if(taskConfig.current_step != Int.MAX_VALUE && messageLog.isNotEmpty()) {
                    val responseBody = JSONUtils.notificationToJsonString(messageLog)
                    extractBodyVariableToCache(action, responseBody, responseBody.toByteArray())
                }

                val actionExec = genActionExec(messageLog, currentStep, respCode, cost)
                actionExecList += actionExec
            }.onFailure {
                LogUtils.error(it)
                val actionExec = genExceptionActionExec(action, ERROR_CODE_PIN_ACTION_EXEC_FAILED, Log.getStackTraceString(it))
                actionExecList += actionExec
                if(action.skip_error) {
                    taskConfig.current_step = ++currentStep
                }else {
                    taskConfig.current_step = Int.MAX_VALUE
                }
            }
            onFinish(actionExecList)
        }

    private fun genActionExec(
        messageLog: MutableList<NotificationMessage>,
        currentStep: Int,
        respCode: Int,
        cost: Long
    ): ActionExec{
        val actionExec = ActionExec().apply {
            this.step = currentStep
            this.index = 1
            this.respCode = respCode
            this.cost = cost
            this.time = System.currentTimeMillis()
            if(messageLog.isNotEmpty()) {
                this.url = messageLog.first().app
                this.respData = JSONUtils.notificationToJsonString(messageLog)
            }
        }
       return actionExec
    }

    private fun haveNextCatchTargetMessage(
        notificationCache: List<NotificationMessage>,
        nextList: List<Next>
    ): List<NotificationMessage> {
        var result: List<NotificationMessage> = mutableListOf()
        var targetMessage: NotificationMessage? = null
        for (notify in notificationCache) {
            for (next in nextList) {
                if (next.step <= 0) continue
                LogUtils.info("next:$next")
                val contain = next.contain
                if (contain.isNotBlank() && notify.from.contains(contain) ||
                    notify.content.contains(contain)
                ) {
                    targetMessage = notify
                    break
                }

                if (next.regexp.isNotBlank()) {
                    val pattern = next.regexp.toRegex()
                    var matcher = pattern.matches(notify.from)
                    if (matcher) {
                        targetMessage = notify
                        break
                    }

                    matcher = pattern.matches(notify.content)
                    if (matcher) {
                        targetMessage = notify
                        break
                    }
                }
            }
            if (targetMessage != null) {
                break
            }
        }
        if (targetMessage != null) {
            result = notificationCache.filter { targetMessage.from == it.from }.toList()
        }

        return result
    }
}