package com.example.vastlib.service

import android.util.Log
import com.example.vastlib.entity.action.*
import com.example.vastlib.entity.report.ActionExec
import com.example.vastlib.entity.task.TaskConfig
import com.example.vastlib.entity.web_socket.WsRequest
import com.example.vastlib.entity.web_socket.WsResponse
import com.example.vastlib.utils.JSONUtils.toJsonString
import com.example.vastlib.utils.LogUtils
import com.example.vastlib.utils.WebSocketClientManager

class WebSocketActionExecService(
    private val action: WebSocketAction, taskConfig: TaskConfig
) : BaseActionExecService(taskConfig) {

    suspend fun executeAction(onFinish: (List<ActionExec>) -> Unit)   {
        val actionExecList: MutableList<ActionExec> = mutableListOf()
        var currentStep: Int = taskConfig.current_step
        kotlin.runCatching {
            val actionRequest = action.request ?: throw NullPointerException("request is null")
            amendActionRequest(actionRequest)
            val wsRequest = WsRequest(
                url = actionRequest.url,
                headers = nameValueToMap(actionRequest.headers).toMutableMap(),
                method = WebSocketClientManager.WEB_SOCKET_REQUEST_METHOD,
                delay = action.delay,
                messages = actionRequest.params
            )

            var wsResponse: WsResponse? = null
            var wsReceiveMsg = ""
            if (action.async) {
                WebSocketClientManager.getInstance().callWebSocketRequest(wsRequest)
                genActionExec(wsRequest, null).apply {
                    respCode = ASYNC_EXEC_CODE
                    actionExecList += this
                }

            } else {
                wsResponse = WebSocketClientManager.getInstance().callWebSocketRequest(wsRequest)
                genActionExec(wsRequest, wsResponse).apply {
                    actionExecList += this
                }
                wsReceiveMsg = wsResponse.data.toString()
                extractBodyVariableToCache(
                    action, wsReceiveMsg, wsReceiveMsg.toByteArray(Charsets.UTF_8)
                )
            }
            taskConfig.current_step = wsResponse?.run {
                when (wsResponse.code) {
                    WEB_SOCKET_CODE -> {
                        getNextStepIndex(action.next, wsReceiveMsg, currentStep)
                    }
                    else -> {
                        if (action.skip_error) {
                            ++currentStep
                        } else {
                            Int.MAX_VALUE
                        }
                    }
                }
            } ?: run {
                ++currentStep
            }
        }.onFailure {
            LogUtils.error(throwable = it)
            genExceptionActionExec(
                action,
                ERROR_CODE_WS_ACTION_EXEC_FAILED,
                Log.getStackTraceString(it)
            ).apply {
                actionExecList += this
            }
            taskConfig.current_step = if (action.skip_error) {
                ++currentStep
            } else {
                Int.MAX_VALUE
            }
        }
        onFinish.invoke(actionExecList)
    }

    private fun genActionExec(wsRequest: WsRequest, wsResponse: WsResponse?): ActionExec {
        val actionExec = ActionExec(
            step = taskConfig.current_step,
            index = 1,
            time = System.currentTimeMillis(),
            url = wsRequest.url,
            method = wsRequest.method,
            reqHeader = wsRequest.headers.toJsonString()
        )

        wsRequest.messages.joinToString(",") { item ->
            item.value
        }.apply {
            actionExec.reqData = this
        }

        wsResponse?.apply {
            actionExec.respCode= wsResponse.code
            actionExec.respData = wsResponse.data?.toString() ?: ""
            actionExec.respHeader = wsResponse.headers.toJsonString()
            actionExec.cost = wsResponse.endTime - wsResponse.startTime
        }
        return actionExec
    }

    private fun amendActionRequest(actionRequest: WebSocketActionRequest) {
        replaceVariableData(actionRequest.headers)
        replaceVariableData(actionRequest.cookies)
        replaceWsParamVariableData(actionRequest.params)

        if (actionRequest.data.isNotBlank()) {
            actionRequest.data = getVariableData(actionRequest.data)
            if (actionRequest.params.isNotEmpty()) {
                WsRequestParam(
                    value = actionRequest.data,
                ).apply {
                    actionRequest.params = listOf(this)
                }

            }
        }

        amendBaseHeader(actionRequest.headers, false).apply {
            actionRequest.headers = this
        }

        getVariableData(actionRequest.url).runCatching{
            actionRequest.url = this
            if (startsWith("wss://")) replace("wss://", "https://") else replace("ws://", "http://")
        }.onSuccess{
            amendCookie(it, actionRequest)
        }
    }

    private fun amendCookie(cookieUrl: String, actionRequest: WebSocketActionRequest) {
        val cookies: MutableSet<NameValue> = mutableSetOf()
        cookieFromCookieManager(cookieUrl).apply {
            if (actionRequest.auto_cookie && isNotEmpty()) {
                cookies += this
            }
        }
        actionRequest.cookies.apply {
            forEach { item ->
                cookies.remove(item)
                cookies += item
            }
        }

        if (cookies.isNotEmpty()) {
            actionRequest.headers += buildCookie(cookies.toList())
        }
    }

    private fun replaceWsParamVariableData(params: List<WsRequestParam>) {
        params.forEach { item ->
            item.value = getVariableData(item.value)
            item.interrupt = getVariableData(item.interrupt)
        }
    }
}