package com.topjohnwu.magisk.ui.safetynet

import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.events.CheckSafetyNetEvent
import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.*
import com.topjohnwu.magisk.utils.set
import org.json.JSONObject

enum class SafetyNetState {
    LOADING, PASS, FAILED, IDLE
}

data class SafetyNetResult(
    val response: JSONObject? = null,
    val dismiss: Boolean = false
)

class SafetynetViewModel : BaseViewModel() {

    @get:Bindable
    var safetyNetTitle = R.string.empty
        set(value) = set(value, field, { field = it }, BR.safetyNetTitle)

    @get:Bindable
    var ctsState = false
        set(value) = set(value, field, { field = it }, BR.ctsState)

    @get:Bindable
    var basicIntegrityState = false
        set(value) = set(value, field, { field = it }, BR.basicIntegrityState)

    @get:Bindable
    var evalType = ""
        set(value) = set(value, field, { field = it }, BR.evalType)

    @get:Bindable
    val isChecking get() = currentState == LOADING
    @get:Bindable
    val isFailed get() = currentState == FAILED
    @get:Bindable
    val isSuccess get() = currentState == PASS

    private var currentState = IDLE
        set(value) = set(value, field, { field = it }, BR.checking, BR.failed, BR.success)

    init {
        cachedResult?.also {
            resolveResponse(SafetyNetResult(it))
        } ?: attest()
    }

    private fun attest() {
        currentState = LOADING
        CheckSafetyNetEvent() {
            resolveResponse(it)
        }.publish()
    }

    fun reset() = attest()

    private fun resolveResponse(response: SafetyNetResult) {
        if (response.dismiss) {
            back()
            return
        }

        response.response?.apply {
            runCatching {
                val cts = getBoolean("ctsProfileMatch")
                val basic = getBoolean("basicIntegrity")
                val eval = optString("evaluationType")
                val result = cts && basic
                cachedResult = this
                ctsState = cts
                basicIntegrityState = basic
                evalType = if (eval.contains("HARDWARE")) "HARDWARE" else "BASIC"
                currentState = if (result) PASS else FAILED
                safetyNetTitle =
                    if (result) R.string.safetynet_attest_success
                    else R.string.safetynet_attest_failure
            }.onFailure {
                currentState = FAILED
                ctsState = false
                basicIntegrityState = false
                evalType = "N/A"
                safetyNetTitle = R.string.safetynet_res_invalid
            }
        } ?: {
            currentState = FAILED
            ctsState = false
            basicIntegrityState = false
            evalType = "N/A"
            safetyNetTitle = R.string.safetynet_api_error
        }()
    }

    companion object {
        private var cachedResult: JSONObject? = null
    }

}
