package app.closer.ui.pairing

import android.util.Log
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.core.navigation.AppRoute
import app.closer.crypto.CoupleEncryptionManager
import app.closer.domain.repository.AuthRepository
import app.closer.domain.repository.InviteRepository
import app.closer.domain.repository.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

data class InviteConfirmUiState(
    val isLoading: Boolean = true,
    val inviterName: String? = null,
    val recoveryPhrase: String = "",
    val error: String? = null,
    val navigateTo: String? = null,
    val isConfirming: Boolean = false,
    val isEncryptedInvite: Boolean = false
)

@HiltViewModel
class InviteConfirmViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val authRepository: AuthRepository,
    private val inviteRepository: InviteRepository,
    private val encryptionManager: CoupleEncryptionManager,
    private val userRepository: UserRepository
) : ViewModel() {

    private val inviteCode: String = savedStateHandle["inviteCode"] ?: ""

    private val _uiState = MutableStateFlow(InviteConfirmUiState())
    val uiState: StateFlow<InviteConfirmUiState> = _uiState.asStateFlow()

    init {
        viewModelScope.launch {
            // Invite details are no longer readable client-side. The Cloud Function
            // will perform server-side validation and acceptance when the user confirms.
            // We show a generic loading state and proceed to confirmation; the inviter name
            // is fetched after a successful acceptance if needed.
            _uiState.update {
                it.copy(
                    isLoading = false,
                    inviterName = "your partner",
                    isEncryptedInvite = true
                )
            }
        }
    }

    fun onPhraseChanged(phrase: String) = _uiState.update { it.copy(recoveryPhrase = phrase, error = null) }

    fun confirmPairing() {
        val acceptorId = authRepository.currentUserId ?: run {
            _uiState.update { it.copy(error = "Not signed in.") }
            return
        }
        val phrase = _uiState.value.recoveryPhrase.trim()
        _uiState.update { it.copy(isConfirming = true, error = null) }
        viewModelScope.launch {
            inviteRepository.acceptInvite(inviteCode, acceptorId, phrase)
                .onSuccess { result ->
                    encryptionManager.unwrapAndStore(result.coupleId, result.wrappedKey, phrase)
                        .onSuccess {
                            val inviterName = runCatching { userRepository.getUser(result.inviterUserId)?.displayName }
                                .onFailure { e -> Log.w(TAG, "Could not load inviter display name", e) }
                                .getOrNull()
                            _uiState.update { it.copy(isConfirming = false, inviterName = inviterName ?: "your partner", navigateTo = AppRoute.HOME) }
                        }
                        .onFailure { e ->
                            _uiState.update { it.copy(isConfirming = false, error = recoveryErrorMessage(e)) }
                        }
                }
                .onFailure { e ->
                    _uiState.update { it.copy(isConfirming = false, error = callableErrorMessage(e)) }
                }
        }
    }

    fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }
    fun dismissError() = _uiState.update { it.copy(error = null) }

    private fun recoveryErrorMessage(e: Throwable): String = when {
        (e.message ?: "").contains("AEADBadTag", ignoreCase = true) ||
        (e.message ?: "").contains("decryption", ignoreCase = true) ->
            "That phrase doesn't match. Ask your partner to recheck it."
        else -> e.message ?: "Couldn't unlock the couple key. Please try again."
    }

    private fun callableErrorMessage(e: Throwable): String {
        val msg = e.message ?: ""
        return when {
            msg.contains("not-found", ignoreCase = true) -> "Code not found. Double-check with your partner."
            msg.contains("failed-precondition", ignoreCase = true) ->
                if (msg.contains("expired", ignoreCase = true)) "This code has expired. Ask your partner to create a new one."
                else if (msg.contains("already been used", ignoreCase = true)) "This code has already been used."
                else if (msg.contains("already paired", ignoreCase = true)) "You are already paired."
                else "Couldn't complete pairing. Please try again."
            msg.contains("permission-denied", ignoreCase = true) -> "You cannot accept your own invite."
            msg.contains("invalid-argument", ignoreCase = true) -> "Enter the recovery phrase your partner shared with you."
            msg.contains("unauthenticated", ignoreCase = true) -> "Not signed in."
            else -> e.message ?: "Couldn't complete pairing. Please try again."
        }
    }

    companion object {
        private const val TAG = "InviteConfirmViewModel"
    }
}
