package moe.lava.neon.ui

import android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import co.touchlab.kermit.Logger
import com.hcaptcha.sdk.HCaptcha
import com.hcaptcha.sdk.HCaptchaConfig
import com.hcaptcha.sdk.HCaptchaSize
import com.hcaptcha.sdk.HCaptchaTokenResponse
import com.hcaptcha.sdk.HCaptchaVerifyParams
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import moe.lava.neon.common.captcha.CaptchaRequest
import moe.lava.neon.common.captcha.CaptchaResponse

private val logger = Logger.withTag("neon.ui.app/captcha")

private const val EXTRA_SITE_KEY = "extra_site_key"
private const val EXTRA_RQ_DATA = "extra_rq_data"
private const val EXTRA_IS_INVISIBLE = "extra_is_invisible"
private const val EXTRA_RESULT_TOKEN = "extra_result_token"
private const val EXTRA_RESULT_ERROR = "extra_result_error"

@Composable
actual fun getCaptchaHandler(): suspend (CaptchaRequest) -> CaptchaResponse {
    val context = LocalContext.current
    val queue = MutableSharedFlow<Pair<String, CaptchaResponse>>()
    val scope = rememberCoroutineScope()

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        val rq = result.data?.getStringExtra(EXTRA_RQ_DATA)
        if (result.resultCode == Activity.RESULT_OK) {
            val token = result.data?.getStringExtra(EXTRA_RESULT_TOKEN)
            scope.launch {
                if (rq == null) {
                    logger.e { "Captcha failed: No rq in result" }
                    queue.emit("" to CaptchaResponse.Failed(Throwable("No rq?")))
                } else if (token == null) {
                    logger.d { "Captcha failed: No token in result" }
                    queue.emit(rq to CaptchaResponse.Failed(Throwable("No token returned?")))
                } else {
                    logger.d { "Captcha success; token $token" }
                    queue.emit(rq to CaptchaResponse.Success(token))
                }
            }
        } else {
            val error = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                result.data?.getSerializableExtra(EXTRA_RESULT_ERROR, Throwable::class.java)
            } else {
                result.data?.getSerializableExtra(EXTRA_RESULT_ERROR) as? Throwable
            }
            logger.e(error) { "Captcha failed" }
            scope.launch {
                queue.emit((rq ?: "") to CaptchaResponse.Failed(error ?: Throwable("No error returned")))
            }
        }
    }

    return { captcha ->
        val intent = Intent(context, HCaptchaActivity::class.java).apply {
            putExtra(EXTRA_SITE_KEY, captcha.captchaSitekey)
            putExtra(EXTRA_RQ_DATA, captcha.captchaRqdata)
            putExtra(EXTRA_IS_INVISIBLE, captcha.shouldServeInvisible)
        }
        launcher.launch(intent)
        queue
            .first { (rqdata) -> rqdata == captcha.captchaRqdata }
            .second
    }
}

class HCaptchaActivity : AppCompatActivity() {
    private val hCaptcha = HCaptcha.getClient(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val siteKey = intent.getStringExtra(EXTRA_SITE_KEY)
            ?: return finishWithError("", IllegalArgumentException("Missing site key"))

        val rqData = intent.getStringExtra(EXTRA_RQ_DATA)
            ?: return finishWithError("", IllegalArgumentException("Missing rq data"))

        val isInvisible = intent.getBooleanExtra(EXTRA_IS_INVISIBLE, false)

        val config = HCaptchaConfig.builder()
            .siteKey(siteKey)
            .size(if (isInvisible) HCaptchaSize.INVISIBLE else HCaptchaSize.NORMAL)
            .build()

        val params = HCaptchaVerifyParams.builder()
            .rqdata(rqData)
            .build()

        hCaptcha
            .addOnOpenListener { logger.d { "hCaptcha is now visible." } }
            .addOnSuccessListener { finishWithSuccess(rqData, it) }
            .addOnFailureListener { finishWithError(rqData, it) }

        hCaptcha.setup(config).verifyWithHCaptcha(params)
    }

    private fun finishWithSuccess(rq: String, res: HCaptchaTokenResponse) {
        logger.d { "Captcha success; token ${res.tokenResult}" }
        val resultIntent = Intent().apply {
            putExtra(EXTRA_RQ_DATA, rq)
            putExtra(EXTRA_RESULT_TOKEN, res.tokenResult)
        }
        setResult(RESULT_OK, resultIntent)
        finish()
    }

    private fun finishWithError(rq: String, exception: Throwable) {
        logger.e(exception) { "Captcha failed" }
        val resultIntent = Intent().apply {
            putExtra(EXTRA_RQ_DATA, rq)
            putExtra(EXTRA_RESULT_ERROR, exception)
        }
        setResult(RESULT_CANCELED, resultIntent)
        finish()
    }
}
