package com.suno.android.captcha import android.app.Activity import com.hcaptcha.sdk.HCaptcha import com.hcaptcha.sdk.HCaptchaConfig import com.hcaptcha.sdk.HCaptchaTokenResponse import com.hcaptcha.sdk.tasks.OnFailureListener import com.hcaptcha.sdk.tasks.OnSuccessListener import com.suno.android.common_core_utils.environment.EnvironmentConstantsProvider import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import javax.inject.Inject import javax.inject.Singleton interface CaptchaManager { fun setActivityContext( activity: Activity, ) fun resetActivityContext() suspend fun requestToken(): String } @Singleton class CaptchaManagerImpl @Inject constructor( private val environmentConstantsProvider: EnvironmentConstantsProvider, ) : CaptchaManager { private val hCaptchaStateFlow: MutableStateFlow = MutableStateFlow(null) private val hCaptchaMutex = Mutex() override fun setActivityContext( activity: Activity, ) { // todo: tomdroid look at this for DI val hCaptchaConfig = HCaptchaConfig.builder() .siteKey(environmentConstantsProvider.environmentConstants.hcaptchaSiteKey) .hideDialog(true) .build() hCaptchaStateFlow.value = HCaptcha.getClient(activity).setup(hCaptchaConfig) } override fun resetActivityContext() { hCaptchaStateFlow.value = null } @OptIn(InternalCoroutinesApi::class) override suspend fun requestToken(): String = hCaptchaMutex.withLock { hCaptchaStateFlow.value?.let { hCaptcha -> suspendCancellableCoroutine { continuation -> lateinit var successListener: OnSuccessListener lateinit var failureListener: OnFailureListener fun cleanup() { hCaptcha.removeOnSuccessListener(successListener) hCaptcha.removeOnFailureListener(failureListener) } successListener = OnSuccessListener { response -> cleanup() response.markUsed() continuation.tryResume(response.tokenResult)?.let { continuation.completeResume(it) } } failureListener = OnFailureListener { e -> cleanup() continuation.tryResumeWithException(e)?.let { continuation.completeResume(it) } } hCaptcha.apply { addOnSuccessListener(successListener) addOnFailureListener(failureListener) }.verifyWithHCaptcha() continuation.invokeOnCancellation { cleanup() } } } ?: throw IllegalStateException("HCaptcha not initialized") } }