package com.adealink.weparty.account.login.auth

import android.app.Activity
import android.content.Intent
import android.content.IntentSender
import androidx.core.app.ActivityCompat.startIntentSenderForResult
import com.adealink.frame.log.Log
import com.adealink.weparty.account.BuildConfig
import com.adealink.weparty.account.constant.GOOGLE_CLIENT_ID
import com.adealink.weparty.account.constant.GOOGLE_CLIENT_ID_DEBUG
import com.adealink.weparty.account.constant.TAG_GOOGLE_AUTH
import com.adealink.weparty.account.login.auth.AuthManager.Companion.REQ_GOOGLE_ONE_TAP
import com.adealink.weparty.account.login.auth.AuthManager.Companion.REQ_GOOGLE_SIGN_IN
import com.adealink.weparty.account.login.data.ThirdType
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.Identity
import com.google.android.gms.auth.api.identity.SignInClient
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.tasks.Task
import java.lang.ref.WeakReference

class GoogleAuth(
    override val activityRef: WeakReference<Activity>?,
    override val authCallback: IAuthCallback?,
) : IAuth {

    companion object {
        private const val ERROR_CODE_SEND_INTENT_EXCEPTION = 10000
        private const val ERROR_CODE_START_ONE_TAP_FAILED = 10001
        private const val ERROR_CODE_NO_ID_TOKEN = 10002
    }

    override var secret: String? = null

    private var oneTapClient: SignInClient? = null
    private var googleSignInClient: GoogleSignInClient? = null

    private var showOneTapUI = true
    val thirdType = ThirdType.GOOGLE

    private fun getOneTapClient(activity: Activity): SignInClient {
        if (oneTapClient != null) {
            return oneTapClient!!
        }

        oneTapClient = Identity.getSignInClient(activity)
        return oneTapClient!!
    }

    private fun getServerClientId(): String {
        return when (BuildConfig.DEBUG) {
            true -> GOOGLE_CLIENT_ID_DEBUG
            else -> GOOGLE_CLIENT_ID
        }
    }

    override fun auth() {
        val activity = activityRef?.get() ?: return
        val available = GoogleApiAvailability.getInstance()
            .isGooglePlayServicesAvailable(activity.applicationContext)
        if (available != ConnectionResult.SUCCESS) {
            authCallback?.onFailed(thirdType, "google service no support", null)
            return
        }

        oneTapSignIn(activity, {
            oneTapSignUp(activity) {
                unOneTapSignIn(activity)
            }
        }, {
            unOneTapSignIn(activity)
        })
    }

    private fun oneTapSignIn(
        activity: Activity,
        signInFailed: () -> Unit,
        sendIntentFailed: () -> Unit,
    ) {
        val signInRequest = BeginSignInRequest.builder()
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    .setServerClientId(getServerClientId())
                    .setFilterByAuthorizedAccounts(true)
                    .build()
            )
            .setAutoSelectEnabled(true)
            .build()
        getOneTapClient(activity).beginSignIn(signInRequest)
            .addOnSuccessListener { result ->
                try {
                    startIntentSenderForResult(
                        activity,
                        result.pendingIntent.intentSender, REQ_GOOGLE_ONE_TAP,
                        null, 0, 0, 0, null
                    )
                } catch (e: IntentSender.SendIntentException) {
                    Log.e(
                        TAG_GOOGLE_AUTH,
                        "oneTapSignIn, SendIntentException, e:${e.localizedMessage}"
                    )
                    sendIntentFailed()
                }
            }
            .addOnCanceledListener {
                Log.i(
                    TAG_GOOGLE_AUTH,
                    "oneTapSignIn, start one tap ui cancel"
                )
                signInFailed()
            }
            .addOnCompleteListener {
                Log.i(
                    TAG_GOOGLE_AUTH,
                    "oneTapSignIn, start one tap ui complete, isSuccessful: ${it.isSuccessful}"
                )

            }
            .addOnFailureListener { e ->
                Log.e(
                    TAG_GOOGLE_AUTH,
                    "oneTapSignIn, start one tap ui failed, e:${e.localizedMessage}"
                )
                signInFailed()
            }
    }

    private fun oneTapSignUp(
        activity: Activity,
        failed: () -> Unit,
    ) {
        val signUpRequest = BeginSignInRequest.builder()
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    .setServerClientId(getServerClientId())
                    .setFilterByAuthorizedAccounts(false)
                    .build()
            )
            .build()
        getOneTapClient(activity).beginSignIn(signUpRequest)
            .addOnSuccessListener {
                try {
                    startIntentSenderForResult(
                        activity,
                        it.pendingIntent.intentSender, REQ_GOOGLE_ONE_TAP,
                        null, 0, 0, 0, null
                    )
                } catch (e: IntentSender.SendIntentException) {
                    Log.e(
                        TAG_GOOGLE_AUTH,
                        "oneTapSignUp, SendIntentException, e:${e.localizedMessage}"
                    )
                    failed()
                }
            }
            .addOnCanceledListener {
                Log.e(
                    TAG_GOOGLE_AUTH,
                    "oneTapSignUp, start one tap ui cancel"
                )
                failed()
            }
            .addOnCompleteListener {
                Log.i(
                    TAG_GOOGLE_AUTH,
                    "oneTapSignUp, start one tap ui complete, isSuccessful:${it.isSuccessful}"
                )
            }
            .addOnFailureListener {
                Log.e(
                    TAG_GOOGLE_AUTH,
                    "oneTapSignUp, start one tap ui failed, e:${it.localizedMessage}"
                )
                failed()
            }
    }

    private fun getGoogleSignInClient(activity: Activity): GoogleSignInClient {
        if (googleSignInClient != null) {
            return googleSignInClient!!
        }

        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getServerClientId())
            .requestEmail()
            .build()
        googleSignInClient = GoogleSignIn.getClient(activity, gso)
        return googleSignInClient!!
    }

    private fun unOneTapSignIn(activity: Activity) {
        val signInIntent = getGoogleSignInClient(activity).signInIntent
        activity.startActivityForResult(signInIntent, REQ_GOOGLE_SIGN_IN)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        when (requestCode) {
            REQ_GOOGLE_ONE_TAP -> {
                try {
                    val credential = oneTapClient?.getSignInCredentialFromIntent(intent)
                    val idToken = credential?.googleIdToken
                    when {
                        idToken != null -> {
                            authCallback?.onSuccess(thirdType, idToken)
                        }
                        else -> {
                            Log.e(TAG_GOOGLE_AUTH, "No ID token")
                            authCallback?.onFailed(thirdType, "no id token", ERROR_CODE_NO_ID_TOKEN)
                        }
                    }
                } catch (e: ApiException) {
                    Log.e(TAG_GOOGLE_AUTH, "Get ID Token Exception, e:${e.localizedMessage}")
                    when (e.statusCode) {
                        CommonStatusCodes.CANCELED -> {
                            // Don't re-prompt the user.
                            showOneTapUI = false
                            authCallback?.onCancel(thirdType)
                        }
                        CommonStatusCodes.NETWORK_ERROR -> {
                            authCallback?.onFailed(thirdType, e.message ?: "", e.statusCode)
                        }
                        else -> {
                            authCallback?.onFailed(thirdType, e.message ?: "", e.statusCode)
                        }
                    }
                }
            }
            REQ_GOOGLE_SIGN_IN -> {
                val task: Task<GoogleSignInAccount> =
                    GoogleSignIn.getSignedInAccountFromIntent(intent)

                handleSignInResult(task)
            }
        }
    }

    private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
        try {
            val account = completedTask.getResult(
                ApiException::class.java
            )
            Log.d(TAG_GOOGLE_AUTH, "handleSignInResult, account.id:${account.id}")
            val token = account.idToken
            if (!account.isExpired && !token.isNullOrEmpty()) {
                authCallback?.onSuccess(thirdType, token)
            }
        } catch (e: ApiException) {
            Log.e(
                TAG_GOOGLE_AUTH,
                "signInResult:failed code=${e.statusCode}, errorMsg:${e.message}"
            )
            if (GoogleSignInStatusCodes.SIGN_IN_CANCELLED == e.statusCode) {
                authCallback?.onCancel(thirdType)
            } else {
                authCallback?.onFailed(thirdType, e.message ?: "", e.statusCode)
            }
        }
    }

    override fun logout() {
        val activity = activityRef?.get() ?: return
        getOneTapClient(activity).signOut()
        getGoogleSignInClient(activity).signOut()
    }

}