package com.develop.step.viewmodel

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.scopeNetLife
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.GsonUtils
import com.blankj.utilcode.util.ToastUtils
import com.blankj.utilcode.util.ZipUtils
import com.develop.base.ext.fromJson
import com.develop.base.ext.globalApp
import com.develop.base.mvvm.BaseViewModel
import com.develop.base.util.FileKit
import com.develop.base.util.MMkvUtils
import com.develop.base.util.ThreadUtils
import com.develop.common.data_repo.FoodDataProvider
import com.develop.common.data_repo.db.entity.DevAccessory
import com.develop.common.data_repo.db.entity.DevRecipePortionSize
import com.develop.common.data_repo.db.entity.UserOnLineRecipes
import com.develop.common.data_repo.net.Api
import com.develop.common.data_repo.net.converter.SerializationConverter
import com.develop.common.data_repo.net.model.response.RecipeDataConfig
import com.develop.common.data_repo.net.model.response.RecipeDetailResult
import com.develop.common.tag.CURRENT_LANGUAGE
import com.develop.common.tag.CURRENT_USER_ID
import com.develop.common.utils.Resource
import com.develop.step.ui.recipes_detail.model.CookDetailInfo
import com.drake.net.Get
import com.drake.net.component.Progress
import com.drake.net.interfaces.ProgressListener
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.io.File

class CookDetailViewModel : BaseViewModel() {
    private val recipeLiveData = MutableLiveData<CookDetailInfo>()
    private val errorLiveData = MutableLiveData<Int>()
    val starCountLiveData = MutableLiveData<Int>()
    val downloadResource = MutableLiveData<Resource<Int>>()
    val portionSizeLiveData = MutableLiveData<DevRecipePortionSize>()
    var recipeNumber: String? = null
    private var mRecipeUrl: String? = null

    //是否线上菜谱
    var isRemote = false


    /**
     * 数据源: 食谱编号
     * 关联数据: [DevRecipe] 食谱基本数据
     *          [DevRecipeAccessory] 食谱需要的配件
     *          [DevAccessory] 单个配件数据
     *          [DevRecipeFood] 食谱原料数据
     *          [DevRecipeNutrition] 食谱营养数据
     *          [DevRecipePortionSize] 食谱原料用量
     */
    fun queryRecipe(number: String, remote: Boolean, recipesEdition: String) {
        isRemote = remote
        this.recipeNumber = number
        if (remote) {
            scopeNetLife {
                Get<RecipeDetailResult>(Api.GET_RECIPES_DETAIL) {
                    addQuery("recipeNumber", number)
                    addQuery("recipeEditon", recipesEdition)
                    addQuery("lang", MMkvUtils.getString(CURRENT_LANGUAGE) ?: "EN")
                }.await().apply {
                    mRecipeUrl = recipeUrl
                    val detailInfo = CookDetailInfo(
                        devRecipe,
                        devAccessorys,
                        devRecipeFoods,
                        devRecipeNutritions,
                        devRecipePortionSizes
                    )
                    portionSizeLiveData.postValue(detailInfo.portionSize.firstOrNull())
                    recipeLiveData.postValue(detailInfo)
                }
            }.catch {
                ToastUtils.showShort(it.message)
            }
            if (FoodDataProvider.isResourceDownload(number)) {
                downloadResource.value = Resource(100, Resource.Status.SUCCESS)
            }
        } else {
            FoodDataProvider.getDatabase().runInTransaction {
                val recipeDao = FoodDataProvider.getDatabase().recipeDao()
                val recipeBean = recipeDao.queryRecipe(number)
                if (recipeBean == null) {
                    // show toast
                    errorLiveData.postValue(ERR_NO_RECIPE_MATCH)
                    return@runInTransaction
                }
                val includeAccessory = mutableListOf<DevAccessory>()
                val accessoryIds = recipeDao.queryAccessoryIds(number)
                for (accessoryId in accessoryIds) {
                    val accessory = recipeDao.queryAccessory(accessoryId.accessoryNumber ?: "")
                    if (accessory != null) {
                        includeAccessory.add(accessory)
                    }
                }
                val includeMaterial = recipeDao.queryFood(number)
                val includeNutrition = recipeDao.queryNutrition(number)
                val includePortionSize = recipeDao.queryPortionSize(number)
                val detailInfo = CookDetailInfo(
                    recipeBean,
                    includeAccessory,
                    includeMaterial,
                    includeNutrition,
                    includePortionSize
                )
                portionSizeLiveData.postValue(detailInfo.portionSize.firstOrNull())
                recipeLiveData.postValue(detailInfo)
            }
        }
    }

    fun getRecipeLiveData(): LiveData<CookDetailInfo> {
        return recipeLiveData
    }

    fun getErrorCodeLiveData(): LiveData<Int> {
        return errorLiveData
    }

    fun getStarCount(recipesId: String) {
        FoodDataProvider.getUserDatabase().runInTransaction {
            val userTag = FoodDataProvider.getUserDatabase().userInfoDao().queryUserTag(
                CURRENT_USER_ID, recipesId
            )
            userTag?.apply {
                starCountLiveData.postValue(starCount)
            }
        }
    }

    fun isDownloading(): Boolean {
        return downloadResource.value?.status == Resource.Status.LOADING
    }

    fun isDownloadSuccess(): Boolean {
        return downloadResource.value?.status == Resource.Status.SUCCESS
    }

    fun getDownloadState(): LiveData<Resource<Int>> {
        return downloadResource
    }

    fun downloadRecipe() {
        if (mRecipeUrl.isNullOrEmpty()) {
            downloadResource.value = Resource(0, Resource.Status.FAILURE)
            return
        }
        downloadResource.value = Resource(0, Resource.Status.LOADING)
        val downloadDir = globalApp().externalCacheDir.toString()
        val downloadName = System.nanoTime().toString()
        scopeNetLife {
            mRecipeUrl?.apply {
                val result = Get<File>(this) {
                    setDownloadFileName(downloadName)
                    setDownloadDir(downloadDir)
                    addDownloadListener(object : ProgressListener() {
                        override fun onProgress(p: Progress) {
                            ThreadUtils.runOnMainThread {
                                downloadResource.value =
                                    Resource(p.progress(), Resource.Status.LOADING)
                            }
                        }
                    })
                }.await()
                if (!result.exists()) {
                    ThreadUtils.runOnMainThread {
                        downloadResource.value = Resource(0, Resource.Status.FAILURE)
                    }
                } else {
                    prepareResource(result)
                }
            }
        }.catch {
            ThreadUtils.runOnMainThread {
                downloadResource.value = Resource(0, Resource.Status.FAILURE)
            }
        }
    }

    private fun prepareResource(file: File) {
        try {
            val dst = ZipUtils.unzipFile(file, FoodDataProvider.getExtRecipeResourceDir())
            FileUtils.delete(file)
            if (dst.isNullOrEmpty()) {
                ThreadUtils.runOnMainThread {
                    downloadResource.value = Resource(0, Resource.Status.FAILURE)
                }
                return
            }
            val jsonFile = FoodDataProvider.getResourceConfigJsonPath(recipeNumber!!)
            if (!jsonFile.exists()) {
                ThreadUtils.runOnMainThread {
                    downloadResource.value = Resource(0, Resource.Status.FAILURE)
                }
                return
            }
            val jsonContent = FileKit.readFileToString(jsonFile)

            val contentData = GsonUtils.fromJson(jsonContent, RecipeDataConfig::class.java)
            contentData.resetAllCodes()
            FoodDataProvider.getDatabase().runInTransaction {
                FoodDataProvider.getDatabase().recipeDao().apply {
                    insertDevAccessorys(contentData.devAccessorys)
                    insertHotTags(contentData.devHotTags)
                    insertDevPortraits(contentData.devPortraits)
                    insertDevRecipeAccessorys(contentData.devRecipeAccessorys)
                    insertDevRecipeCategorys(contentData.devRecipeCategorys)
                    insertDevRecipeCookingSteps(contentData.devRecipeCookingSteps)
                    insertDevRecipeFoods(contentData.devRecipeFoods)
                    insertDevRecipeNutritions(contentData.devRecipeNutritions)
                    insertDevRecipePortionSizes(contentData.devRecipePortionSizes)
                    insertDevRecipeRelTags(contentData.devRecipeRelTags)
                    insertDevRecipeTags(contentData.devRecipeTags)
                    insertDevRecipes(contentData.devRecipes)
                }
                ThreadUtils.runOnMainThread {
                    downloadResource.value = Resource(100, Resource.Status.SUCCESS)
                }
            }
            FoodDataProvider.getUserDatabase().runInTransaction {
                contentData.devRecipes.getOrNull(0)?.also {
                    FoodDataProvider.getUserDatabase().userInfoDao().insertOnlineRecipe(
                        UserOnLineRecipes(CURRENT_USER_ID, it.number ?: "")
                    )
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            ThreadUtils.runOnMainThread {
                downloadResource.value = Resource(0, Resource.Status.FAILURE)
            }
        }
    }

    companion object {
        const val ERR_NO_RECIPE_MATCH = -1
    }
}