package com.ama.core.architecture.base

import android.view.View
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ama.core.architecture.interfaces.LoadStateUiStateOwner
import com.ama.core.architecture.interfaces.UiStateOwner
import com.ama.core.architecture.message.MessageController
import com.ama.core.architecture.message.MessageDisplayManager
import com.ama.core.architecture.message.controller.DefaultMessageController
import com.ama.core.architecture.stateview.LoadStateUiState
import com.ama.core.architecture.stateview.isNormalException
import com.ama.core.architecture.util.toErrorMessage
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

abstract class BaseViewModel<UiState> : ViewModel(), UiStateOwner<UiState>,
    LoadStateUiStateOwner,
    MessageDisplayManager {
    // 消息控制者，子类可覆盖其实现。
    override val messageController: MessageController = DefaultMessageController()

    // 加载状态-UiState
    override val loadStateUiState = MutableStateFlow<LoadStateUiState?>(null)

    // UiState配置，子类可覆盖其实现。
    override val uiState: StateFlow<UiState> by lazy {
        uiStateFlow.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = uiStateInitialValue,
        )
    }

    fun requestAsync(asyncFun: suspend () -> Unit) {
        // 效果说明：不隐藏内容，仅展示加载中，加载失败改为展示消息提示（普通异常）。
        requestAsyncSingleOnlyHint(
            errorCallback = {
                // 网络规则提示消息用于UI展示，普通异常消息用于消息提示（如：Toast），其它状态不显示。
                if (error.isNormalException()) {
                    // 普通异常消息用于消息提示
                    showMessage(error.toErrorMessage())
                }
            },
            asyncFun = asyncFun
        )
    }
}




private fun <Data> BaseViewModel<*>.requestAsyncBase(
    // 默认值，用于统一修改下面状态的默认值。
    isShowContentLayoutAllStateDefault: Boolean?,
    isShowStateViewAllStateDefault: Boolean,
    // loading
    isShowContentLayoutLoading: Boolean? = isShowContentLayoutAllStateDefault,
    isShowStateViewLoading: Boolean = isShowStateViewAllStateDefault,
    isStateViewLoadingDialog: Boolean = false,
    loadingCallback: (LoadStateUiState.Loading.() -> Unit)? = null,
    // error
    isShowContentLayoutError: Boolean? = isShowContentLayoutAllStateDefault,
    isShowStateViewError: Boolean = isShowStateViewAllStateDefault,
    errorCallback: (LoadStateUiState.Error.() -> Unit)? = null,
    // empty
    isShowContentLayoutEmpty: Boolean? = isShowContentLayoutAllStateDefault,
    isSuccessEmpty: Data.() -> Boolean = { false }, // 判断Success下什么情况下为空，默认不支持为空。
    isShowStateViewEmpty: Boolean = isShowStateViewAllStateDefault,
    emptyCallback: (LoadStateUiState.Empty.() -> Unit)? = null,
    // success
    successCallback: (LoadStateUiState.Success.() -> Unit)? = null,

    // asyncFun
    asyncFun: suspend () -> Data,
) {
    viewModelScope.launch {
        // Loading
        val loading =
            LoadStateUiState.Loading(
                isShowContentLayoutLoading,
                isShowStateViewLoading,
                isStateViewLoadingDialog
            )
        loadStateUiState.value = loading
        loadingCallback?.invoke(loading)
        try {
            val data = asyncFun()
            if (isSuccessEmpty(data)) {
                // Empty
                val empty = LoadStateUiState.Empty(isShowContentLayoutEmpty, isShowStateViewEmpty)
                loadStateUiState.value = empty
                emptyCallback?.invoke(empty)
            } else {
                // Success
                // 加载成功，一定要显示内容布局、显示StateView-Success，防止其它操控loadStateUiState的设置了隐藏内容布局。
                val success =
                    LoadStateUiState.Success(isShowContentLayout = true, isShowStateView = true)
                loadStateUiState.value = success
                successCallback?.invoke(success)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            // Error
            // 重试
            val retry: (v: View) -> Unit = {
                requestAsyncBase(
                    isShowContentLayoutAllStateDefault,
                    isShowStateViewAllStateDefault,
                    // loading
                    isShowContentLayoutLoading,
                    isShowStateViewLoading,
                    isStateViewLoadingDialog,
                    loadingCallback,
                    // error
                    isShowContentLayoutError,
                    isShowStateViewError,
                    errorCallback,
                    // empty
                    isShowContentLayoutEmpty,
                    isSuccessEmpty,
                    isShowStateViewEmpty,
                    emptyCallback,
                    // success
                    successCallback,
                    // asyncFun
                    asyncFun
                )
            }
            val error =
                LoadStateUiState.Error(isShowContentLayoutError, isShowStateViewError, e, retry)
            loadStateUiState.value = error
            errorCallback?.invoke(error)
        }
    }
}

/**
 * 请求异步-单网络-仅提示。即：内容布局全部显示，StateView仅展示加载中，加载失败默认改为展示消息提示。
 */
fun <Data> BaseViewModel<*>.requestAsyncSingleOnlyHint(
    errorCallback: (LoadStateUiState.Error.() -> Unit)? = null,
    asyncFun: suspend () -> Data,
) {
    if (loadStateUiState.value is LoadStateUiState.Loading) {
        // 网络请求中，直接返回。
        return
    }
    // 效果说明：不隐藏内容，仅展示加载中，加载失败改为展示消息提示。
    requestAsyncBase(
        // 所有状态默认显示内容布局
        isShowContentLayoutAllStateDefault = true,
        // 所有状态默认不显示StateView（Loading除外，下面设置）
        isShowStateViewAllStateDefault = false,
        // 设置显示StateView-Loading
        isShowStateViewLoading = true,
        // 设置显示StateView-Error，改为定制（先用参数的，然后再用自己的消息提示）
        errorCallback = errorCallback ?: { showMessage(kotlin.error("").toString()) },
        // 请求
        asyncFun = asyncFun
    )
}

/**
 * 请求异步-单网络-展示所有状态。即：StateView全部显示，内容布局仅加载中、成功显示。
 */
fun <Data> BaseViewModel<*>.requestAsyncSingleShowAllState(
    asyncFun: suspend () -> Data,
) {
    if (loadStateUiState.value is LoadStateUiState.Loading) {
        // 网络请求中，直接返回。
        return
    }
    requestAsyncBase(
        // 所有状态默认不显示内容布局（Loading除外，下面设置）
        isShowContentLayoutAllStateDefault = false,
        // 所有状态默认显示StateView
        isShowStateViewAllStateDefault = true,
        // Loading，不操作内容（防止设置为true后，强制显示内容布局，导致显示闪烁）。
        isShowContentLayoutLoading = null,
        // 请求
        asyncFun = asyncFun
    )
}