package com.example.vastlib.utils

import android.icu.lang.UCharacter.GraphemeClusterBreak.T
import com.example.vastlib.entity.action.*
import com.example.vastlib.entity.http.Request
import com.example.vastlib.entity.http.Response
import com.example.vastlib.entity.report.TaskExec
import com.example.vastlib.entity.request.BaseRequest
import com.example.vastlib.entity.request.ReportTaskRequest
import com.example.vastlib.entity.response.TaskResponse
import com.example.vastlib.entity.task.Task
import com.example.vastlib.pin.NotificationMessage
import com.example.vastlib.utils.JSONUtils.toJsonString
import org.json.JSONArray
import org.json.JSONObject
import org.xml.sax.Parser
import kotlin.math.log

object JSONUtils {


    fun Request.toJsonString(): String = JSONObject().let {
        it.put("url", url)
        it.put("method", method)
        headers.toJson().apply {
            it.put("headers", this)
        }
        it.put("body", String(body))
        it.toString()
    }

    fun notificationToJsonString(notifications:List<NotificationMessage>):String  {
        val jsonArray = JSONArray()
        for (n in notifications) {
            JSONObject().let {
                it.put("content", n.content)
                it.put("from", n.from)
                it.put("time", n.time)
                it.put("app", n.app)
            }.apply {
                jsonArray.put(this)
            }
        }
        return jsonArray.toString()
    }

    fun Response.toJsonString(): String = JSONObject().let {
        it.put("code", code)
        it.put("start_time", startTime)
        it.put("end_time", endTime)
        it.put("data", String(data))
        JSONObject().let { jsonObject ->
            headers.map{ h ->
                jsonObject.put("${h.key}", "${h.value}")
            }
        }.apply {
            it.put("header", this)
        }
        it.toString()
    }

    fun Map<String, List<String>>.mapListToJsonString(): String = JSONObject().let { obj ->
        filter { it.key.isNullOrBlank() != true }.mapValues {
            JSONArray().let { array ->
                it.value.map { v -> array.put(v) }
                obj.put(it.key, array)
            }
        }
        obj.toString()
    }

    fun Map<String, String>.toJsonString(): String = this.toJson().toString()

    fun Map<String, String>.toJson(): JSONObject =
        JSONObject().let {obj->
            filter { it.key.isNullOrBlank() != true }.map { h ->
                    obj.put(h.key, h.value)
            }
         obj
        }

    fun <A> List<A>.toJson(): JSONArray = JSONArray().let { array ->
        forEach {
            when (it) {
                is NameValue -> {
                    array.put(JSONObject().put(it.name, it.value))
                }

                is TaskExec -> {
                    JSONObject().let { obj ->
                        obj.put("taskId", it.taskId)
                        obj.put("taskVer", it.taskVer)
                        obj.put("taskUid", it.taskUid)
                        obj.put("lastStep", it.lastStep)
                    }.let { task ->
                        JSONArray().let { logs ->
                            it.logs.map { l ->
                                JSONObject().let { log ->
                                    log.put("step", l.step)
                                    log.put("index", l.index)
                                    log.put("time", l.time)
                                    log.put("url", l.url)
                                    log.put("cost", l.cost)
                                    log.put("method", l.method)
                                    log.put("reqHeader", l.reqHeader)
                                    log.put("reqData", l.reqData)
                                    log.put("respCode", l.respCode)
                                    log.put("respHeader", l.respHeader)
                                    log.put("respData", l.respData)
                                }.apply {
                                    logs.put(this)
                                }
                            }
                            logs
                        }.run {
                            task.put("logs", this)
                        }
                    }.run {
                        array.put(this)
                    }
                }
            }
        }
        array
    }

    fun <T>List<T>.toJsonString() = toJson().toString()

    fun ReportTaskRequest.toJsonString(): String {
        return this.toJson().put("taskLogs", this.taskLogs.toJson()).toString()
    }

    fun BaseRequest.toJsonString() = toJson().toString()

    fun BaseRequest.toJson(): JSONObject = JSONObject().let {
        it.put("userId", userId)
        it.put("pkg", pkg)
        it.put("affId", affId)
        it.put("subId", subId)
        it.put("androidVer", androidVer)
        it.put("sdkVer", sdkVer)
        it.put("appVer", appVer)
        it.put("countryCode", countryCode)
        it.put("telcoCode", telcoCode)
        it.put("netType", netType)
        it.put("recvFlag", recvFlag)
    }

    class TaskReposeParser(private val data: ByteArray) : Parser<TaskResponse?> {
        @Throws(Exception::class)
        override fun parse(): TaskResponse? {
            return runCatching {
                val taskResponse = TaskResponse()
                JSONObject(String(data)).run {
                    taskResponse.userAgent = optString("userAgent", "")
                    taskResponse.acceptLanguage = optString("acceptLanguage", "")
                    taskResponse.accept = optString("accept", "")
                    taskResponse.reportUrl = optString("reportUrl", "")
                    taskResponse.requestInterval = optInt("requestInterval")
                    taskResponse.result = optBoolean("result")
                    optJSONArray("tasks")?.run {
                        (0 until length()).forEach { index ->
                            val task = getJSONObject(index)
                            val t = Task(
                                taskId = task.optInt("taskId"),
                                taskVer = task.optInt("taskVer"),
                                taskUid = task.optLong("taskUid"),
                            )
                            taskResponse.tasks += t
                            task.optJSONArray("actions")?.run {
                                (0 until length()).forEach {
                                    val action = getJSONObject(it)
                                    with(action) {
                                        val type = optInt("type")
                                        val delay = optInt("delay")
                                        val skipError = optBoolean("skip_error")
                                        val async = optBoolean("async")
                                        val disconnectWs = optBoolean("disconnect_ws")
                                        when (type) {
                                            BaseAction.ACTION_TYPE_HTTP -> {
                                                parseHttpAction(
                                                    type,
                                                    delay,
                                                    skipError,
                                                    async,
                                                    disconnectWs,
                                                    this,
                                                )
                                            }
                                            BaseAction.ACTION_TYPE_WS -> {
                                                parseWebSocketAction(
                                                    type,
                                                    delay,
                                                    skipError,
                                                    async,
                                                    disconnectWs,
                                                    this,
                                                )
                                            }
                                            BaseAction.ACTION_TYPE_PIN -> {
                                                parsePinAction(
                                                    type,
                                                    delay,
                                                    skipError,
                                                    async,
                                                    disconnectWs,
                                                    this
                                                )
                                            }
                                            else -> {
                                                null
                                            }
                                        }?.apply {
                                            t.actions += this
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                taskResponse
            }.getOrNull()
        }

        private fun parsePinAction(
            type: Int,
            delay: Int,
            skipError: Boolean,
            async: Boolean,
            disconnectWs: Boolean,
            jsonObject: JSONObject,
        ):PinAction{
           val pinAction = PinAction(
               type = type,
               delay = delay,
               skip_error = skipError,
               async =  async,
               disconnect_ws = disconnectWs
           )

            jsonObject.optJSONArray("next")?.run {
                (0 until length()).forEach { index ->
                    pinAction.next += optJSONObject(index).run {
                        Next(
                            contain = optString("contain"),
                            step = optInt("step"),
                            regexp = optString("regexp")
                        )
                    }
                }
            }

            jsonObject.optJSONArray("params")?.run {
                (0 until length()).forEach {
                    getJSONObject(it).apply {
                        val arv = VarExtractRule(
                            expr = optString("expr"),
                            variable = optString("variable"),
                            rule = optInt("rule")
                        )
                        pinAction.params += arv
                    }
                }
            }

            pinAction.filter = jsonObject.optBoolean("filter", true)

            return pinAction
        }

        private fun parseWebSocketAction(
            type: Int,
            delay: Int,
            skipError: Boolean,
            async: Boolean,
            disconnectWs: Boolean,
            jsonObject: JSONObject,
        ): WebSocketAction {
            val webSocketAction = WebSocketAction(
                type = type,
                delay = delay,
                skip_error = skipError,
                async = async,
                disconnect_ws = disconnectWs,
            )
            webSocketAction.request = jsonObject.optJSONObject("request")?.run {
                val webSocketActionRequest = WebSocketActionRequest(
                    url = optString("url"),
                    data = optString("data"),
                    auto_cookie = optBoolean("auto_cookie")
                )

                optJSONArray("headers")?.parseNameValue { header ->
                    webSocketActionRequest.headers += header
                }

                optJSONArray("params")?.apply {
                    (0 until this.length()).forEach { index ->
                        webSocketActionRequest.params += getJSONObject(
                            index
                        ).run {
                            WsRequestParam(
                                wait_time = optLong("wait_time"),
                                name = optString("name"),
                                value = optString("value"),
                                interrupt = optString("interrupt")
                            )
                        }
                    }
                }

                optJSONArray("cookies")?.parseNameValue { cookie ->
                    webSocketActionRequest.cookies += cookie
                }
                webSocketAction.request = webSocketActionRequest
                webSocketActionRequest
            }

            webSocketAction.response = jsonObject.optJSONObject("response")?.run {
                val webSocketActionResponse = WebSocketActionResponse()
                optJSONArray("params")?.run {
                    (0 until length()).forEach { index ->
                        webSocketActionResponse.params += optJSONObject(
                            index
                        ).run {
                            VarExtractRule(
                                expr = optString("expr"),
                                variable = optString("variable"),
                                rule = optInt("rule")
                            )
                        }
                    }
                }
                webSocketActionResponse
            }
            jsonObject.optJSONArray("next")?.run {
                (0 until length()).forEach { index ->
                    webSocketAction.next += optJSONObject(index).run {
                        Next(
                            contain = optString("contain"),
                            step = optInt("step"),
                            regexp = optString("regexp")
                        )
                    }
                }
            }
            return webSocketAction
        }

        private fun parseHttpAction(
            type: Int,
            delay: Int,
            skipError: Boolean,
            async: Boolean,
            disconnectWs: Boolean,
            jsonObject: JSONObject,
        ): HttpAction {
            val httpAction = HttpAction(
                type = type,
                delay = delay,
                skip_error = skipError,
                async = async,
                disconnect_ws = disconnectWs
            )
            jsonObject.optJSONObject("request")?.run {
                val url = optString("url", "")
                val method = optString("method", "")
                val autoCookie = optBoolean("auto_cookie", true)
                val actionRequest = HttpActionRequest(
                    url = url,
                    method = method,
                    auto_cookie = autoCookie,
                )

                optJSONArray("headers")?.parseNameValue { nv ->
                    actionRequest.headers += nv
                }

                optJSONArray("params")?.parseNameValue { nv ->
                    actionRequest.params += nv
                }

                optJSONArray("cookies")?.parseNameValue { nv ->
                    actionRequest.cookies += nv
                }

                optString("data", "").run {
                    actionRequest.data = this
                }

                httpAction.request = actionRequest
            }
            jsonObject.optJSONObject("response")?.run {
                val actionResponse = HttpActionResponse()
                optJSONArray("headers")?.parseNameVariable { nv ->
                    actionResponse.headers += nv
                }

                optJSONArray("cookies")?.parseNameVariable { nv ->
                    actionResponse.cookies += nv
                }

                optJSONArray("params")?.run {
                    (0 until length()).forEach {
                        getJSONObject(it).apply {
                            val arv = VarExtractRule(
                                expr = optString("expr"),
                                variable = optString("variable"),
                                rule = optInt("rule")
                            )
                            actionResponse.params += arv
                        }
                    }
                }


                httpAction.response = actionResponse

            }

            jsonObject.optJSONArray("next")?.run {
                (0 until length()).forEach {index->
                    optJSONObject(index)?.apply {
                        httpAction.next += Next(
                            contain = optString("contain"),
                            step = optInt("step"),
                            regexp = optString("regexp")
                        )
                    }
                }
            }

            return httpAction
        }

        val parseNameValue: JSONArray.(callback: (NameValue) -> Unit) -> Unit =
            { callback: (NameValue) -> Unit ->
                (0 until length()).forEach { index ->
                    getJSONObject(index).run {
                        callback(
                            NameValue(
                                optString("name", ""), optString("value", "")
                            )
                        )
                    }

                }
            }

        val parseNameVariable: JSONArray.(callback: (NameVariable) -> Unit) -> Unit =
            { callback: (NameVariable) -> Unit ->
                (0 until length()).forEach { index ->
                    getJSONObject(index).run {
                        callback(
                            NameVariable(
                                optString("name", ""), optString("variable", "")
                            )
                        )
                    }

                }
            }
    }

    interface Parser<T> {
        fun parse(): T
    }
}