package com.pixelized.biblib.ui.screen.authentication.viewModel

import android.app.Activity
import android.content.Intent
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.*
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.common.api.ApiException
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.network.data.query.AuthLoginQuery
import com.pixelized.biblib.repository.credential.ICredentialRepository
import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository
import com.pixelized.biblib.ui.screen.authentication.AuthenticationErrorUio
import com.pixelized.biblib.ui.screen.authentication.AuthenticationUio
import com.pixelized.biblib.utils.exception.MissingGoogleTokenException
import com.pixelized.biblib.utils.exception.MissingTokenException
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class AuthenticationViewModel @Inject constructor(
    private val credentialRepository: ICredentialRepository,
    private val googleSignIn: IGoogleSingInRepository,
    private val client: IBibLibClient,
) : ViewModel() {
    private var launcher: ActivityResultLauncher<Intent>? = null
    private var authenticationJob: Job? = null

    private val _authenticationError = MutableSharedFlow<AuthenticationErrorUio>()
    val authenticationError: Flow<AuthenticationErrorUio> get() = _authenticationError

    private val _authenticationProcess = mutableStateOf<AuthenticationUio?>(null)
    val authenticationProcess: State<AuthenticationUio?> get() = _authenticationProcess

    val isLoading: Boolean by derivedStateOf {
        authenticationProcess.value is AuthenticationUio.Progress
    }

    //////////////////////////////////////
    // region: Login with BibLibClient

    fun login(login: String, password: String) {
        authenticationJob?.cancel()
        authenticationJob = viewModelScope.launch(Dispatchers.IO) {
            _authenticationProcess.value = AuthenticationUio.Progress
            try {
                val query = AuthLoginQuery(username = login, password = password)
                val response = client.service.login(query)
                val idToken = response.token ?: throw MissingTokenException()
                client.token = idToken
                credentialRepository.bearer = response.token
                _authenticationProcess.value = AuthenticationUio.Done
            } catch (exception: Exception) {
                Log.e("AuthenticationViewModel", exception.message, exception)
                _authenticationProcess.value = null
                _authenticationError.emit(
                    AuthenticationErrorUio.Login(),
                )
            }
        }
    }

    // endregion
    //////////////////////////////////////
    // region: Login with Google

    @Composable
    fun PrepareLoginWithGoogle() {
        launcher = rememberLauncherForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) {
            if (it.resultCode == Activity.RESULT_OK) {
                authenticationJob?.cancel()
                authenticationJob = viewModelScope.launch(Dispatchers.IO) {
                    _authenticationProcess.value = AuthenticationUio.Progress
                    try {
                        val task = GoogleSignIn.getSignedInAccountFromIntent(it.data)
                        val account = task.getResult(ApiException::class.java)
                        val googleToken = account?.idToken ?: throw MissingGoogleTokenException()
                        val response = client.service.loginWithGoogle(token = googleToken)
                        val token = response.token ?: throw MissingTokenException()
                        client.token = token
                        _authenticationProcess.value = AuthenticationUio.Done
                    } catch (exception: Exception) {
                        Log.e("AuthenticationViewModel", exception.message, exception)
                        _authenticationProcess.value = null
                        _authenticationError.emit(AuthenticationErrorUio.GoogleLogin())
                    }
                }
            }
        }
    }

    fun loginWithGoogle() {
        launcher?.launch(googleSignIn.client.signInIntent)
    }

    // endregion
    //////////////////////////////////////
}