package top.r3944realms.ltdmanager.whitelist

import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.HttpTimeout
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import top.r3944realms.ltdmanager.core.client.IClient
import top.r3944realms.ltdmanager.core.client.response.IFailedResponse
import top.r3944realms.ltdmanager.core.client.response.ResponseResult
import top.r3944realms.ltdmanager.core.config.YamlConfigLoader
import top.r3944realms.ltdmanager.whitelist.data.WhitelistPlayerInfo
import top.r3944realms.ltdmanager.whitelist.request.WhitelistAction
import top.r3944realms.ltdmanager.whitelist.request.WhitelistActionRequest
import top.r3944realms.ltdmanager.whitelist.request.WhitelistListRequest
import top.r3944realms.ltdmanager.whitelist.request.WhitelistSystemRequest
import top.r3944realms.ltdmanager.whitelist.response.FailedWhitelistSystemResponse
import top.r3944realms.ltdmanager.whitelist.response.WhitelistApiResponse
import top.r3944realms.ltdmanager.whitelist.response.WhitelistSystemResponse
import java.util.PriorityQueue

class WhitelistSystemClient private constructor()
    : IClient<WhitelistSystemRequest, WhitelistSystemQueueItem, WhitelistSystemResponse, FailedWhitelistSystemResponse> {

    private val client = HttpClient(CIO) {
        expectSuccess = false
        install(HttpTimeout) {
            requestTimeoutMillis = 30_000
            connectTimeoutMillis = 10_000
            socketTimeoutMillis = 15_000
        }
    }

    private val whitelistSystemConfig = YamlConfigLoader.loadWhitelistSystemConfig()

    private val semaphore = Semaphore(3)
    private val requestMutex = Mutex()
    private val requestQueue = PriorityQueue<WhitelistSystemQueueItem>(compareBy { it.priority })
    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())

    init {
        init()
    }

    override fun getBaseUrl(): String = whitelistSystemConfig.url!!

    override fun getType(): String = "WhitelistSystemClient"

    override fun getClient(): HttpClient = client

    override fun getSemaphore(): Semaphore = semaphore

    override fun getRequestMutex(): Mutex = requestMutex

    override fun getResponseQueue(): PriorityQueue<WhitelistSystemQueueItem> = requestQueue

    override fun getScope(): CoroutineScope = scope

    override fun createFailureResponse(exception: Exception?): IFailedResponse {
        return FailedWhitelistSystemResponse.Default(exception?.stackTraceToString() ?: "ERROR")
    }

    override fun addToQueue(
        request: WhitelistSystemRequest,
        deferredC: CompletableDeferred<ResponseResult<WhitelistSystemResponse, FailedWhitelistSystemResponse>>,
        priority: Int,
        maxRetries: Int
    ): WhitelistSystemQueueItem {
        val element = WhitelistSystemQueueItem(request, deferredC, priority, maxRetries, false)
        requestQueue.add(element)
        return element
    }

    // -- 便捷方法 --
    suspend fun listApprovedAll(): List<WhitelistPlayerInfo> {
        return when (val result = submitRequest(WhitelistListRequest())) {
            is ResponseResult.Success -> {
                val apiResponse = result.response as? WhitelistApiResponse
                apiResponse?.data ?: emptyList()
            }
            is ResponseResult.Failure -> emptyList()
        }
    }
    suspend fun listApproved(): List<WhitelistPlayerInfo> {
        return when (val result = submitRequest(WhitelistListRequest())) {
            is ResponseResult.Success -> {
                val apiResponse = result.response as? WhitelistApiResponse
                apiResponse?.data ?: emptyList()
            }
            is ResponseResult.Failure -> emptyList()
        }
    }

    suspend fun approve(playerId: Int): Boolean =
        callAction(WhitelistAction.APPROVE, playerId)

    suspend fun reject(playerId: Int): Boolean =
        callAction(WhitelistAction.REJECT, playerId)

    suspend fun remove(playerId: Int): Boolean =
        callAction(WhitelistAction.REMOVE, playerId)

    private suspend fun callAction(action: WhitelistAction, playerId: Int): Boolean {
        return when (val result = submitRequest(WhitelistActionRequest(playerId, action))) {
            is ResponseResult.Success -> {
                val apiResponse = result.response as? WhitelistApiResponse
                apiResponse?.code == 200
            }
            is ResponseResult.Failure -> false
        }
    }

    companion object {
        fun create(): WhitelistSystemClient = WhitelistSystemClient()
    }
}
