package com.omixlab.lckcontrol.ui.login

import android.app.Activity
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.omixlab.lckcontrol.data.local.TokenStore
import com.omixlab.lckcontrol.data.remote.LckApiService
import com.omixlab.lckcontrol.data.remote.MetaCallbackRequest
import com.omixlab.lckcontrol.data.remote.RefreshRequest
import com.meta.horizon.platform.ovr.Core
import com.meta.horizon.platform.ovr.requests.Request
import com.meta.horizon.platform.ovr.requests.Users
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val tokenStore: TokenStore,
    private val apiService: LckApiService,
) : ViewModel() {

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    private val _error = MutableStateFlow<String?>(null)
    val error: StateFlow<String?> = _error.asStateFlow()

    private val _loginSuccess = MutableStateFlow(false)
    val loginSuccess: StateFlow<Boolean> = _loginSuccess.asStateFlow()

    fun isLoggedIn(): Boolean = tokenStore.isLoggedIn()

    /** Try to validate existing session, refresh if needed, or do full Quest SDK login */
    fun attemptAutoLogin(activity: Activity) {
        viewModelScope.launch {
            _isLoading.value = true
            _error.value = null
            try {
                // If we have a JWT, try validating it
                if (tokenStore.isLoggedIn()) {
                    try {
                        apiService.getMe()
                        Log.d(TAG, "Existing session valid")
                        _loginSuccess.value = true
                        return@launch
                    } catch (e: Exception) {
                        Log.w(TAG, "Session validation failed, will try refresh", e)
                    }
                }

                // Try refresh token
                val refreshToken = tokenStore.getRefreshToken()
                if (refreshToken != null) {
                    try {
                        val response = apiService.refreshSession(RefreshRequest(refreshToken))
                        tokenStore.saveSession(response.accessToken, response.refreshToken)
                        Log.d(TAG, "Token refresh successful")
                        _loginSuccess.value = true
                        return@launch
                    } catch (e: Exception) {
                        Log.w(TAG, "Token refresh failed, falling back to full login", e)
                        tokenStore.clearSession()
                    }
                }

                // Full Quest SDK login
                doQuestLogin(activity)
            } catch (e: Exception) {
                Log.e(TAG, "Auto-login failed", e)
                _error.value = e.message ?: "Auto-login failed"
            } finally {
                _isLoading.value = false
            }
        }
    }

    fun loginWithQuest(activity: Activity) {
        viewModelScope.launch {
            _isLoading.value = true
            _error.value = null
            try {
                doQuestLogin(activity)
            } catch (e: Exception) {
                Log.e(TAG, "Quest login failed", e)
                _error.value = e.message ?: "Login failed"
            } finally {
                _isLoading.value = false
            }
        }
    }

    private suspend fun doQuestLogin(activity: Activity) {
        if (!Core.isInitialized()) {
            Log.d(TAG, "Async initializing Platform SDK with appId=$QUEST_APP_ID")
            val initResult = awaitWithPump { Core.asyncInitialize(QUEST_APP_ID, activity.applicationContext) }
            Log.d(TAG, "Platform SDK initialized: $initResult")
        }

        val user = awaitWithPump { Users.getLoggedInUser() }
        val numericId = user.getID().toString()
        val oculusId = user.oculusID

        val userId = if (user.getID() != 0L) numericId else oculusId
        if (userId.isNullOrEmpty()) {
            throw Exception("Platform SDK returned no user identifier. " +
                "Make sure the app is installed from the Horizon store " +
                "and your account is a test user for this app.")
        }

        var nonce = ""
        try {
            val proof = awaitWithPump { Users.getUserProof() }
            nonce = proof.value
        } catch (e: Exception) {
            Log.w(TAG, "getUserProof failed (DUC pending?), proceeding without nonce", e)
        }

        val response = apiService.metaCallback(
            MetaCallbackRequest(
                userId = userId,
                nonce = nonce,
                deviceInfo = android.os.Build.MODEL,
            )
        )

        tokenStore.saveSession(response.accessToken, response.refreshToken)
        Log.d(TAG, "Login successful!")
        _loginSuccess.value = true
    }

    fun clearError() {
        _error.value = null
    }

    private suspend fun <T> awaitWithPump(
        block: () -> Request<T>,
    ): T = suspendCoroutine { cont ->
        var completed = false
        block()
            .onSuccess { result: T ->
                if (!completed) {
                    completed = true
                    cont.resume(result)
                }
            }
            .onError { error ->
                if (!completed) {
                    completed = true
                    cont.resumeWithException(Exception("SDK error ${error.code}: ${error.message}"))
                }
            }

        Thread {
            val timeout = System.currentTimeMillis() + 15_000
            while (!completed && System.currentTimeMillis() < timeout) {
                try {
                    val msg = Core.popSDKMessage()
                    if (msg != null) Request.handleMessage(msg)
                } catch (e: Exception) {
                    Log.w(TAG, "Message pump error", e)
                }
                Thread.sleep(50)
            }
            if (!completed) {
                completed = true
                cont.resumeWithException(Exception("Platform SDK request timed out after 15s"))
            }
        }.start()
    }

    companion object {
        private const val TAG = "LoginViewModel"
        const val QUEST_APP_ID = "25653777174321448"
    }
}
