package com.develop.step.details

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.ToastUtils
import com.blankj.utilcode.util.ZipUtils
import com.develop.food.base.api.FoodRepository
import com.develop.food.base.api.response.RecipeDataConfig
import com.develop.food.base.http.Resource
import com.develop.food.base.http.observeStatus
import com.develop.food.base.repo.CURRENT_USER_ID
import com.develop.food.base.repo.FoodDataProvider
import com.develop.food.base.repo.entity.DevAccessory
import com.develop.food.base.repo.entity.DevRecipePortionSize
import com.develop.food.base.repo.entity.UserOnLineRecipes
import com.develop.food.base.utils.DownloadUtil
import com.develop.food.base.utils.FileKit
import com.develop.food.base.utils.MmkvUtils
import com.develop.food.base.utils.ThreadUtils
import com.develop.step.model.CookDetailInfo
import com.google.gson.Gson
import java.io.File

class CookDetailViewModel(application: Application) : AndroidViewModel(application) {

    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 recipeUrl: 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) {
            FoodRepository.recipesDetail(number, recipesEdition).observeStatus(
                onSuccess = {
                    if (it == null) {
                        ToastUtils.showShort("Load failure")
                        return@observeStatus
                    }
                    recipeUrl = it.recipeUrl
                    val detailInfo = CookDetailInfo(
                        it.devRecipe,
                        it.devAccessorys,
                        it.devRecipeFoods,
                        it.devRecipeNutritions,
                        it.devRecipePortionSizes,
                        it.devRecipeCookingSteps,
                    )
                    portionSizeLiveData.postValue(detailInfo.portionSize.firstOrNull())
                    recipeLiveData.postValue(detailInfo)
                },
                onFailed = {
                    it?.desc?.let { msg ->
                        ToastUtils.showShort(msg)
                    }
                }
            )
            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 inclueCookStep = recipeDao.queryCookingStep(number);
                val detailInfo = CookDetailInfo(
                    recipeBean,
                    includeAccessory,
                    includeMaterial,
                    includeNutrition,
                    includePortionSize,
                    inclueCookStep
                )
                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 (recipeUrl.isNullOrEmpty()) {
            downloadResource.value = Resource(0, Resource.Status.FAILURE)
            return
        }
        downloadResource.value = Resource(0, Resource.Status.LOADING)
        val downloadDir = getApplication<Application>().externalCacheDir.toString()
        val downloadName = System.nanoTime().toString()
        DownloadUtil.get().download(recipeUrl, downloadDir, downloadName,
            object : DownloadUtil.OnDownloadListener {
                override fun onDownloadSuccess(file: File) {
                    if (!file.exists()) {
                        ThreadUtils.runOnMainThread {
                            downloadResource.value = Resource(0, Resource.Status.FAILURE)
                        }
                    } else {
                        prepareResource(file)
                    }
                }

                override fun onDownloading(progress: Int) {
                    ThreadUtils.runOnMainThread {
                        downloadResource.value = Resource(progress, Resource.Status.LOADING)
                    }
                }

                override fun onDownloadFailed(e: Exception?) {
                    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 = Gson().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
    }
}