package com.ustadmobile.core.viewmodel.signup

import com.ustadmobile.core.MR
import com.ustadmobile.core.account.LearningSpace
import com.ustadmobile.core.account.SendConsentRequestToParentUseCase
import com.ustadmobile.core.domain.invite.EnrollToCourseFromInviteCodeUseCase
import com.ustadmobile.core.domain.localaccount.GetLocalAccountsSupportedUseCase
import com.ustadmobile.core.domain.credentials.CreatePasskeyUseCase
import com.ustadmobile.core.impl.UstadMobileSystemCommon
import com.ustadmobile.core.impl.appstate.AppUiState
import com.ustadmobile.core.impl.appstate.LoadingUiState
import com.ustadmobile.core.impl.nav.UstadSavedStateHandle
import com.ustadmobile.core.util.ext.appendSelectedAccount
import com.ustadmobile.core.util.ext.putFromSavedStateIfPresent
import com.ustadmobile.core.view.UstadView
import com.ustadmobile.core.viewmodel.UstadEditViewModel
import com.ustadmobile.core.viewmodel.clazz.list.ClazzListViewModel
import com.ustadmobile.core.viewmodel.contententry.list.ContentEntryListViewModel
import com.ustadmobile.core.viewmodel.person.child.ChildProfileListViewModel
import com.ustadmobile.core.viewmodel.person.registerminorwaitforparent.RegisterMinorWaitForParentViewModel
import com.ustadmobile.core.viewmodel.person.registerminorwaitforparent.RegisterMinorWaitForParentViewModel.Companion.ARG_REFERER_SCREEN
import com.ustadmobile.core.viewmodel.signup.SignUpViewModel.Companion.ARG_IS_MINOR
import com.ustadmobile.core.viewmodel.signup.SignUpViewModel.Companion.REGISTRATION_ARGS_TO_PASS
import com.ustadmobile.door.ext.doorPrimaryKeyManager
import com.ustadmobile.lib.db.entities.Person
import com.ustadmobile.lib.db.entities.PersonParentJoin
import com.ustadmobile.lib.db.entities.PersonPicture
import com.ustadmobile.lib.db.entities.ext.shallowCopy
import io.github.aakira.napier.Napier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.kodein.di.DI
import org.kodein.di.direct
import org.kodein.di.instance
import org.kodein.di.instanceOrNull
import org.kodein.di.on


data class OtherSignUpOptionSelectionUiState(
    val showCreateLocaleAccount: Boolean = false,
    val person: Person? = null,
    val personPicture: PersonPicture? = null,
    val passkeySupported: Boolean = true,
    val errorText: String? = null,
)

class OtherSignUpOptionSelectionViewModel(
    di: DI,
    savedStateHandle: UstadSavedStateHandle,
    destName: String = DEST_NAME
) : UstadEditViewModel(di, savedStateHandle, destName) {

    private val _uiState: MutableStateFlow<OtherSignUpOptionSelectionUiState> =
        MutableStateFlow(OtherSignUpOptionSelectionUiState())

    private val serverUrl = savedStateHandle[UstadView.ARG_LEARNINGSPACE_URL]?: "http://localhost"
    private val isParent = savedStateHandle[IS_PARENT].toBoolean()
    private val isMinor = savedStateHandle[ARG_IS_MINOR].toBoolean()

    private val getLocalAccountsSupportedUseCase: GetLocalAccountsSupportedUseCase by instance()

    private val createPasskeyUseCase: CreatePasskeyUseCase? by di.on(LearningSpace(serverUrl)).instanceOrNull()

    private var nextDestination: String =
        savedStateHandle[UstadView.ARG_NEXT] ?: ClazzListViewModel.DEST_NAME_HOME

    val uiState: Flow<OtherSignUpOptionSelectionUiState> = _uiState.asStateFlow()

    private val enrollToCourseFromInviteCodeUseCase: EnrollToCourseFromInviteCodeUseCase =
        di.on(LearningSpace(serverUrl)).direct.instance()

    val sendConsentRequestToParentUseCase : SendConsentRequestToParentUseCase =
        di.on(LearningSpace(serverUrl)).direct.instance()
    init {
        viewModelScope.launch {
            val person = savedStateHandle.getJson(ARG_PERSON, Person.serializer()) ?: Person()
            val personPic = savedStateHandle.getJson(ARG_PERSON_PROFILE_PIC, PersonPicture.serializer()) ?: PersonPicture()
            _uiState.update { prev ->
                prev.copy(
                    person = person,
                    personPicture = personPic,
                    passkeySupported = createPasskeyUseCase != null,
                )
            }
        }

        loadingState = LoadingUiState.INDETERMINATE
        val title =
            systemImpl.getString(MR.strings.other_options)
        if (getLocalAccountsSupportedUseCase.invoke()) {
            _uiState.update { prev->
                prev.copy(
                    showCreateLocaleAccount = true
                )
            }
        }
        _appUiState.update {
            AppUiState(
                title = title,
                userAccountIconVisible = false,
                hideBottomNavigation = true,
                hideAppBar =false,
                navigationVisible = false,
            )
        }


    }

    fun onSignUpWithPasskey() {
        viewModelScope.launch {
            val uid = activeDb.doorPrimaryKeyManager.nextIdAsync(Person.TABLE_ID)
            val savePerson = _uiState.value.person ?: return@launch

            savePerson.personUid = uid

            val createPasskeyResult = createPasskeyUseCase?.invoke(
                username = savePerson.username.toString()
            )
            when(createPasskeyResult){
                is CreatePasskeyUseCase.PasskeyCreatedResult -> {
                    viewModelScope.launch {
                        accountManager.registerWithPasskey(
                            serverUrl,
                            createPasskeyResult.authenticationResponseJSON,
                            savePerson,
                            _uiState.value.personPicture,
                            isMinor
                        )

                        if (isParent) {
                            navController.navigate(
                                ChildProfileListViewModel.DEST_NAME,
                                args = buildMap {
                                    put(ARG_NEXT, nextDestination)
                                    putAllFromSavedStateIfPresent(REGISTRATION_ARGS_TO_PASS)
                                    putFromSavedStateIfPresent(ARG_NEXT)
                                }
                            )

                        }else if (isMinor){
                            sendConsentAndNavigateToMinorWaitScreen(false)
                        }
                        else {
                            enrollToCourseFromInviteUid(savePerson.personUid)
                            val goOptions = UstadMobileSystemCommon.UstadGoOptions(clearStack = true)
                            Napier.d { "AddSignUpPresenter: go to next destination: $nextDestination" }
                            navController.navigateToViewUri(
                                nextDestination.appendSelectedAccount(
                                    savePerson.personUid,
                                    LearningSpace(accountManager.activeLearningSpace.url)
                                ),
                                goOptions
                            )

                        }
                    }

                }
                is CreatePasskeyUseCase.Error ->{
                    _uiState.update { prev ->
                        prev.copy(
                            errorText = createPasskeyResult.message,
                        )
                    }
                }
                is CreatePasskeyUseCase.UserCanceledResult,
                null->{
                    //do nothing
                }
            }

        }

    }


    private suspend fun sendConsentAndNavigateToMinorWaitScreen(showUsernamePassword: Boolean) {
        try {
            val savePerson = _uiState.value.person ?: throw IllegalStateException("child details are empty")
            val parentContact = savedStateHandle[SignUpViewModel.ARG_PARENT_CONTACT]
            val parentPersonParentJoin = PersonParentJoin().shallowCopy {
                ppjMinorPersonUid = savePerson.personUid
            }
            val ppjUid = activeRepoWithFallback.personParentJoinDao().upsertAsync(parentPersonParentJoin)
            sendConsentRequestToParentUseCase(
                SendConsentRequestToParentUseCase.SendConsentRequestToParentRequest(
                    childFullName = savePerson.fullName(),
                    childDateOfBirth = savePerson.dateOfBirth,
                    childGender = savePerson.gender,
                    parentContact = parentContact?:"",
                    ppjUid = ppjUid
                )
            )
            val args = mutableMapOf<String, String>().also {
                it[RegisterMinorWaitForParentViewModel.ARG_USERNAME] = _uiState.value.person?.username ?: ""
                it[RegisterMinorWaitForParentViewModel.ARG_SHOW_USERNAME_PASSWORD] =
                    showUsernamePassword.toString()
                it[RegisterMinorWaitForParentViewModel.ARG_PARENT_CONTACT] =
                    parentContact?:""
                it[RegisterMinorWaitForParentViewModel.ARG_PASSWORD] =  ""
                it.putFromSavedStateIfPresent(savedStateHandle, UstadView.ARG_POPUPTO_ON_FINISH)
                it[ARG_REFERER_SCREEN] = savedStateHandle[ARG_REFERER_SCREEN]?:""
            }
            navController.navigate(
                viewName = RegisterMinorWaitForParentViewModel.DEST_NAME,
                args = args,
                goOptions = UstadMobileSystemCommon.UstadGoOptions(clearStack = true)
            )
        } catch (e: Exception) {
        Napier.e("Error : ${e.message}", e)
        }
}

    fun onClickCreateLocalAccount() {
        loadingState = LoadingUiState.INDETERMINATE

        viewModelScope.launch {
            try {
                accountManager.createLocalAccount()
                val goOptions = UstadMobileSystemCommon.UstadGoOptions(clearStack = true)
                navController.navigate(ContentEntryListViewModel.DEST_NAME_HOME, emptyMap(), goOptions)
            } catch (e: Exception) {
                Napier.e("Error during login: ${e.message}", e)
            } finally {
                loadingState = LoadingUiState.NOT_LOADING
            }
        }
    }
    fun onclickSignUpWithUsernameAdPassword() {

        val args = buildMap {
            put(SignUpViewModel.SIGN_WITH_USERNAME_AND_PASSWORD, "true")
            putAllFromSavedStateIfPresent(REGISTRATION_ARGS_TO_PASS)
            putFromSavedStateIfPresent(ARG_NEXT)
            put(
                ARG_PERSON,
                json.encodeToString( Person.serializer(),_uiState.value.person?:Person())
            )
            put(
                ARG_PERSON_PROFILE_PIC,
                json.encodeToString( PersonPicture.serializer(),_uiState.value.personPicture?:PersonPicture())
            )
            put(IS_PARENT,
                savedStateHandle[IS_PARENT].toString()
            )
        }

        navController.navigate(SignupEnterUsernamePasswordViewModel.DEST_NAME, args)


    }

    suspend fun enrollToCourseFromInviteUid(personUid: Long) {
        try {
            val viewUri= savedStateHandle[UstadView.ARG_NEXT]
            if (viewUri != null&&viewUri.contains("ClazzInviteRedeem")) {
                nextDestination = ClazzListViewModel.DEST_NAME_HOME
                enrollToCourseFromInviteCodeUseCase.invoke(
                    viewUri =viewUri,
                    personUid = personUid
                )
            }
        } catch (e: Exception) {
            print(e.message)
        }

    }

    companion object {


        const val DEST_NAME = "otheroption"
        const val ARG_PERSON = "otheroptionperson"
        const val ARG_PERSON_PROFILE_PIC = "otheroptionpersonprofilepic"
        const val IS_PARENT = "isparent"




    }

}