package com.example.modifier.http.api

import android.util.Log
import com.example.modifier.baseTag
import com.example.modifier.http.isTimeoutException
import com.example.modifier.http.ktorClient
import com.example.modifier.http.request.RcsNumberRequest
import com.example.modifier.http.response.RcsNumberResponse
import io.ktor.client.call.body
import io.ktor.client.plugins.resources.get
import io.ktor.client.plugins.resources.post
import io.ktor.client.plugins.resources.put
import io.ktor.client.plugins.retry
import io.ktor.client.plugins.timeout
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.resources.Resource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import kotlin.coroutines.cancellation.CancellationException
import kotlin.coroutines.coroutineContext
import kotlin.time.Duration.Companion.seconds

@Resource("api/rcs-number")
class RcsNumberApi() {
    @Resource("{id}")
    class Id(val parent: RcsNumberApi = RcsNumberApi(), val id: Int) {
        @Resource("delete")
        class Delete(val parent: Id)

        @Resource("otpState")
        class OtpState(val parent: Id)

        @Resource("configured")
        class Configured(val parent: Id)

        @Resource("wasted")
        class Wasted(val parent: Id)

        @Resource("stockFlag/{flag}")
        class UpdateStockFlag(val parent: Id, val flag: Int)
    }

    companion object {
        private const val TAG = "$baseTag/RcsNumberApi"
        suspend fun getRcsNumber(
            deviceId: String,
            taskId: Int? = null,
            pinCountry: String?,
            store: Boolean? = false
        ): RcsNumberResponse {
            val req = RcsNumberRequest(
                deviceId = deviceId,
                taskId = taskId,
                country = pinCountry,
                store = store
            )
            val response = ktorClient.put(RcsNumberApi()) {
                contentType(ContentType.Application.Json)
                setBody(req)
                timeout {
                    requestTimeoutMillis = 60 * 1000
                    socketTimeoutMillis = 60 * 1000
                }
                retry {
                    maxRetries = 3600
                    retryOnExceptionIf { _, cause ->
                        when {
                            cause.isTimeoutException() -> true
                            cause is CancellationException -> false
                            else -> true
                        }
                    }
                    retryIf { _, response ->
                        response.status.value.let {
                            when (it) {
                                425, 429, 502, 503, 504 -> true
                                else -> false
                            }
                        }
                    }
                    delayMillis { 1000 }
                }
            }
            return response.body<RcsNumberResponse>()
        }

        fun notifyOtpState(id: Int) {
            CoroutineScope(Dispatchers.IO).launch {
                runCatching {
                    ktorClient.post(Id.OtpState(Id(RcsNumberApi(), id)))
                }.onFailure { e ->
                    Log.e(TAG, "Send OtpState Error: ${e.message}", e)
                }
            }
        }

        fun notifyConfigured(id: Int) {
            CoroutineScope(Dispatchers.IO).launch {
                runCatching {
                    ktorClient.post(Id.Configured(Id(RcsNumberApi(), id)))
                }.onFailure { e ->
                    Log.e(TAG, "Send Configured Error: ${e.message}", e)
                }
            }
        }

        suspend fun notifyWasted(id: Int) {
            CoroutineScope(coroutineContext).launch {
                runCatching {
                    ktorClient.post(Id.Wasted(Id(RcsNumberApi(), id)))
                }.onFailure { e ->
                    Log.e(TAG, "Send Wasted Error: ${e.message}", e)
                }
            }
        }

        suspend fun waitForOtp(id: Int): String? {
            return withTimeoutOrNull(60.seconds) {
                while (true) {
                    runCatching {
                        val rcsNumber =
                            ktorClient.get(Id(id = id))
                                .body<RcsNumberResponse>()
                        Log.i(TAG, "wait for otp response: $rcsNumber")
                        if (rcsNumber.status == RcsNumberResponse.STATUS_SUCCESS) {
                            val match = Regex("Your Messenger verification code is G-(\\d{6})")
                                .find(rcsNumber.message!!)
                            if (match != null) {
                                val otp = match.groupValues[1]
                                Log.i(TAG, "OTP: $otp")
                                return@withTimeoutOrNull otp
                            }
                        } else if (rcsNumber.status == RcsNumberResponse.STATUS_EXPIRED) {
                            return@withTimeoutOrNull null
                        }
                    }.onFailure { e ->
                        Log.e(TAG, "wait for otp Error: ${e.stackTrace}")
                    }
                    delay(2.seconds)
                }
                null
            }
        }

        suspend fun updateStockFlag(id: Int, flag: Int) {
            try {
                ktorClient.put(Id.UpdateStockFlag(Id(RcsNumberApi(), id), flag))
            } catch (e: Exception) {
                Log.e(TAG, "Update Stock Flag Error: ${e.message}", e)
            }
        }
    }
}