@file:OptIn(DelicateCoroutinesApi::class)

package moe.protocol.servlet

import android.os.Bundle
import com.tencent.mobileqq.app.QQAppInterface
import com.tencent.mobileqq.msf.core.MsfCore
import com.tencent.mobileqq.pb.ByteStringMicro
import com.tencent.qphone.base.remote.ToServiceMsg
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import moe.fuqiuluo.xposed.helper.PacketHandler
import moe.fuqiuluo.xposed.helper.internal.DynamicReceiver
import moe.fuqiuluo.xposed.helper.internal.IPCRequest
import moe.protocol.servlet.utils.PlatformUtils
import mqq.app.MobileQQ
import tencent.im.oidb.oidb_sso
import kotlin.concurrent.timer
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

abstract class BaseSvc {
    companion object {
        val currentUin: String
            get() = app.currentAccountUin

        val app: QQAppInterface
            get() = MobileQQ.getMobileQQ().waitAppRuntime() as QQAppInterface

        fun createToServiceMsg(cmd: String): ToServiceMsg {
            return ToServiceMsg("mobileqq.service", app.currentAccountUin, cmd)
        }

        suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray): ByteArray? {
            return suspendCoroutine { continuation ->
                val seq = MsfCore.getNextSeq()
                val timer = timer(initialDelay = 5000L, period = 5000L) {
                    GlobalScope.launch(Dispatchers.Default) {
                        PacketHandler.unregisterLessHandler(seq)
                        continuation.resume(null)
                    }
                }
                GlobalScope.launch(Dispatchers.Default) {
                    DynamicReceiver.register(IPCRequest(cmd, seq) {
                        val buffer = it.getByteArrayExtra("buffer")!!
                        timer.cancel()
                        continuation.resume(buffer)
                    })
                }
                sendOidb(cmd, cmdId, serviceId, data, seq)
            }
        }

        suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray): ByteArray? {
            return suspendCoroutine { continuation ->
                val seq = MsfCore.getNextSeq()
                val timer = timer(initialDelay = 5000L, period = 5000L) {
                    GlobalScope.launch(Dispatchers.Default) {
                        PacketHandler.unregisterLessHandler(seq)
                        continuation.resume(null)
                    }
                }
                GlobalScope.launch(Dispatchers.Default) {
                    DynamicReceiver.register(IPCRequest(cmd, seq) {
                        val buffer = it.getByteArrayExtra("buffer")!!
                        timer.cancel()
                        continuation.resume(buffer)
                    })
                    sendBuffer(cmd, isPb, data, seq)
                }
            }
        }

        fun sendOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1) {
            val to = createToServiceMsg(cmd)
            val oidb = oidb_sso.OIDBSSOPkg()
            oidb.uint32_command.set(cmdId)
            oidb.uint32_service_type.set(serviceId)
            oidb.bytes_bodybuffer.set(ByteStringMicro.copyFrom(buffer))
            oidb.str_client_version.set(PlatformUtils.getClientVersion(MobileQQ.getContext()))
            to.putWupBuffer(oidb.toByteArray())
            to.addAttribute("req_pb_protocol_flag", true)
            if (seq != -1) {
                to.addAttribute("shamrock_seq", seq)
            }
            app.sendToService(to)
        }

        fun sendBuffer(cmd: String, isPb: Boolean, buffer: ByteArray, seq: Int) {
            val toServiceMsg = ToServiceMsg("mobileqq.service", app.currentUin, cmd)
            toServiceMsg.putWupBuffer(buffer)
            toServiceMsg.addAttribute("req_pb_protocol_flag", isPb)
            toServiceMsg.addAttribute("shamrock_seq", seq)
            app.sendToService(toServiceMsg)
        }
    }

    protected fun send(toServiceMsg: ToServiceMsg) {
        app.sendToService(toServiceMsg)
    }

    protected fun sendExtra(cmd: String, builder: (Bundle) -> Unit) {
        val toServiceMsg = createToServiceMsg(cmd)
        builder(toServiceMsg.extraData)
        app.sendToService(toServiceMsg)
    }

    protected fun sendPb(cmd: String, buffer: ByteArray, seq: Int) {
        val toServiceMsg = createToServiceMsg(cmd)
        toServiceMsg.putWupBuffer(buffer)
        toServiceMsg.addAttribute("req_pb_protocol_flag", true)
        toServiceMsg.addAttribute("shamrock_seq", seq)
        app.sendToService(toServiceMsg)
    }
}