package com.gh.base

import android.app.Application
import android.content.Intent
import android.graphics.Bitmap
import android.media.ThumbnailUtils
import android.provider.MediaStore
import android.text.TextUtils
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.entity.ErrorEntity
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.entity.QuoteCountEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
import com.gh.gamecenter.video.upload.OnUploadListener
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import io.reactivex.disposables.Disposable
import okhttp3.ResponseBody
import retrofit2.HttpException
import java.io.File
import java.io.FileOutputStream
import kotlin.collections.set

// TODO: 移动到module_bbs模块
abstract class BaseRichEditorViewModel(application: Application) : AndroidViewModel(application) {
    val mApi: ApiService = RetrofitManager.getInstance().api
    val processDialog = MediatorLiveData<WaitingDialogFragment.WaitingDialogData>()
    val chooseImagesUpload = MutableLiveData<LinkedHashMap<String, String>>()
    val chooseImagesUploadSuccess = MutableLiveData<LinkedHashMap<String, String>>()
    var uploadImageSubscription: Disposable? = null
    val mapImages = HashMap<String, String>()
    val localVideoList = ArrayList<LocalVideoEntity>()
    val uploadVideoErrorList = ArrayList<LocalVideoEntity>()
    var currentUploadingVideo: LocalVideoEntity? = null
    var type: String = "" //游戏论坛：game_bbs  官方论坛：official_bbs
    private var mUploadVideoListener: UploadVideoListener? = null
    val TITLE_MIN_LENGTH = 6
    val MIN_TEXT_LENGTH = 6
    val MAX_TEXT_LENGTH = 10000
    val FILE_HOST = "file:///"
    var id = ""//视频标记
    var videoId = ""//更改封面视频id
    val quoteCountEntity = QuoteCountEntity()//数据上报用

    fun setUploadVideoListener(uploadVideoListener: UploadVideoListener) {
        this.mUploadVideoListener = uploadVideoListener
    }

    //检查图片是否符合规则并上传图片
    fun uploadPic(data: Intent) {
        val uris = Matisse.obtainResult(data)
        val rawImgUrlList = ArrayList<String>() // 需要上传图片的原始地址列表
        val uploadingImgList = ArrayList<String>() // 正在上传图片的地址列表（已压缩处理）
        val compressedImgUrlList = ArrayList<String>() // 压缩处理后图片的地址列表

        for (uri in uris) {
            val picturePath = PathUtils.getPath(getApplication(), uri)
            if (picturePath != null) {
                if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) {
                    val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024
                    val application: Application = getApplication()
                    Utils.toast(
                        getApplication(),
                        application.getString(R.string.pic_max_hint, count)
                    )
                    continue
                }
                Utils.log("picturePath = $picturePath")
                rawImgUrlList.add(picturePath)
            } else {
                Utils.log("picturePath is null")
            }
        }
        if (rawImgUrlList.size == 0) return
        val imageType = when (getRichType()) {
            RichType.ARTICLE -> UploadImageUtils.UploadType.community_article
            RichType.QUESTION -> UploadImageUtils.UploadType.question
            else -> UploadImageUtils.UploadType.poster
        }
        uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(
            imageType,
            rawImgUrlList,
            false,
            object : UploadImageUtils.OnUploadImageListListener {
                override fun onProgress(total: Long, progress: Long) {}

                override fun onCompressSuccess(imageUrls: List<String>) {
                    val chooseImageMd5Map = LinkedHashMap<String, String>()
                    imageUrls.forEach {
                        chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = ""
                    }
                    uploadingImgList.addAll(imageUrls)
                    compressedImgUrlList.addAll(imageUrls)
                    chooseImagesUpload.postValue(chooseImageMd5Map)
                }

                override fun onSingleSuccess(imageUrlMap: Map<String, String>) {
                    imageUrlMap.forEach {
                        if (uploadingImgList.contains(it.key)) {
                            uploadingImgList.remove(it.key)
                        }
                    }
                    val map = LinkedHashMap<String, String>()
                    for (key in imageUrlMap.keys) {
                        map[MD5Utils.getUrlMD5(key)] = FILE_HOST + key.decodeURI()
                        mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrlMap[key] ?: ""
                    }
                    chooseImagesUploadSuccess.value = map
                }

                override fun onSuccess(imageUrlMap: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) {
                    imageUrlMap.forEach {
                        if (uploadingImgList.contains(it.key)) {
                            uploadingImgList.remove(it.key)
                        }
                    }

                    var errorSize = compressedImgUrlList.size - imageUrlMap.size
                    if (errorSize > 0 || uploadingImgList.isNotEmpty()) {
                        val errorImageMap = LinkedHashMap<String, String>()
                        for (key in errorMap.keys) {
                            errorImageMap[MD5Utils.getUrlMD5(key)] = ""
                        }

                        for (rawImgUrl in compressedImgUrlList) {
                            if (!imageUrlMap.containsKey(rawImgUrl)) {
                                errorImageMap[MD5Utils.getUrlMD5(rawImgUrl)] = ""
                            }
                        }
                        errorSize = if (errorMap.isEmpty()) {
                            errorImageMap.size
                        } else {
                            errorMap.size + errorImageMap.size
                        }

                        // value为空会删除PlaceholderImage
                        chooseImagesUploadSuccess.value = errorImageMap

                        if (handleUploadError(errorSize, errorMap)) return

                        if (errorSize > 0) {
                            ToastUtils.showToast(errorSize.toString() + "张图片上传失败")
                        }
                    }
                }

                override fun onError(errorMap: Map<String, Exception>) {
                    val errorSize = uploadingImgList.size
                    if (uploadingImgList.size > 0) {
                        val map = LinkedHashMap<String, String>()
                        for (key in uploadingImgList) {
                            map[MD5Utils.getUrlMD5(key)] = ""
                        }
                        // value为空会删除PlaceholderImage
                        chooseImagesUploadSuccess.value = map
                    }

                    if (handleUploadError(errorSize, errorMap)) return

                    if (errorSize == 0) return

                    if (errorSize == 1) {
                        ToastUtils.showToast("图片上传失败")
                    } else {
                        ToastUtils.showToast(errorSize.toString() + "张图片上传失败")
                    }
                }
            })
    }

    /**
     * 处理上传错误 (包括超时 cancel 的异常)
     * @return 如果 toast 过了就返回 true
     */
    private fun handleUploadError(
        errorSize: Int,
        errorMap: Map<String, Exception>
    ): Boolean {
        for (error in errorMap.values) {
            if (error is HttpException && error.code() == 403) {
                val e = error.response()?.errorBody()?.string()?.toObject<ErrorEntity>()
                if (e != null && e.code == 403017) {
                    ToastUtils.showToast(errorSize.toString() + "张图片的宽或高超过限制，请裁剪后上传")
                } else {
                    ToastUtils.showToast(errorSize.toString() + "张违规图片上传失败")
                }
                return true
            }
        }

        return false
    }

    fun uploadPoster(picturePath: String) {
        processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", true))
        uploadImageSubscription =
            UploadImageUtils.compressAndUploadImage(
                UploadImageUtils.UploadType.poster,
                picturePath,
                false,
                object : UploadImageUtils.OnUploadImageListener {
                    override fun onSuccess(imageUrl: String) {
                        patchVideoPoster(imageUrl)
                    }

                    override fun onError(e: Throwable?) {
                        handleUploadPosterResult(true)
                    }

                    override fun onProgress(total: Long, progress: Long) {

                    }
                })
    }

    private fun patchVideoPoster(poster: String) {
        if (id.isEmpty() || videoId.isEmpty()) return
        val map = hashMapOf("poster" to poster, "type" to getVideoType())
        mApi.patchInsertVideo(videoId, map.toRequestBody())
            .compose(observableToMain())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)
                    mUploadVideoListener?.changePoster(id, poster)
                    handleUploadPosterResult(false)
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    handleUploadPosterResult(true)
                }
            })
    }

    private fun handleUploadPosterResult(isFailure: Boolean = false) {
        processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
        if (isFailure) {
            ToastUtils.showToast("封面更改失败")
        }
        id = ""
        videoId = ""
    }

    fun deleteVideo(id: String) {
        if (localVideoList.isNotEmpty()) {
            val video = localVideoList.find { it.id == id }
            if (video != null) {
                if (UploadManager.isUploading(video.filePath)) {
                    UploadManager.cancelTask(video.filePath)
                }
                localVideoList.remove(video)
            }
        }
        if (uploadVideoErrorList.isNotEmpty()) {
            val video = uploadVideoErrorList.find { it.id == id }
            if (video != null) {
                uploadVideoErrorList.remove(video)
            }
        }
        if (currentUploadingVideo?.id == id) {
            currentUploadingVideo = null
            uploadVideo()
        }
    }

    fun uploadVideo() {
        if (currentUploadingVideo != null) return
        if (localVideoList.isEmpty()) return
        currentUploadingVideo = localVideoList[0]
        UploadManager.createUploadTask(currentUploadingVideo?.filePath
            ?: "", object : OnUploadListener {
            override fun onProgressChanged(
                uploadFilePath: String,
                currentSize: Long,
                totalSize: Long,
                speed: Long
            ) {
                runOnUiThread {
                    val percent = (currentSize * 100 / totalSize.toFloat()).roundTo(1)
                    currentUploadingVideo?.id?.let {
                        mUploadVideoListener?.updateVideoProgress(it, percent.toString())
                    }
                }
            }

            override fun onUploadSuccess(uploadFilePath: String, url: String) {
                if (currentUploadingVideo != null) {
                    postVideoPosterAndInfo(uploadFilePath, url)
                }
            }

            override fun onUploadFailure(uploadFilePath: String, errorMsg: String) {
                uploadVideoFailure()
            }
        })
    }

    private fun postVideoPosterAndInfo(uploadFilePath: String, url: String) {
        val localVideoPoster =
            getApplication<Application>().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg"
        try {
            val bmp = ThumbnailUtils.createVideoThumbnail(
                uploadFilePath,
                MediaStore.Images.Thumbnails.MINI_KIND
            )
            // bmp 可能为空
            FileOutputStream(localVideoPoster).use { out ->
                bmp?.compress(Bitmap.CompressFormat.PNG, 100, out)
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
            ToastUtils.showToast("视频封面操作失败")
            uploadVideoFailure()
            return
        }
        uploadImageSubscription =
            UploadImageUtils.compressAndUploadImage(
                UploadImageUtils.UploadType.poster,
                localVideoPoster,
                false,
                object : UploadImageUtils.OnUploadImageListener {
                    override fun onSuccess(imageUrl: String) {
                        postVideoInfo(url, imageUrl)
                    }

                    override fun onError(e: Throwable?) {
                        uploadVideoFailure()
                    }

                    override fun onProgress(total: Long, progress: Long) {

                    }
                })
    }

    private fun postVideoInfo(url: String, poster: String) {
        val map = HashMap<String, Any>().apply {
            put("poster", poster)
            put("url", url)
            put("format", currentUploadingVideo?.format ?: "")
            put("size", currentUploadingVideo?.size ?: 0)
            put("length", (currentUploadingVideo?.duration ?: 0) / 1000)
            put("type", getVideoType())
        }
        val requestBody = map.toRequestBody()
        mApi.insertVideo(requestBody)
            .compose(observableToMain())
            .subscribe(object : Response<JsonObject>() {
                override fun onResponse(response: JsonObject?) {
                    super.onResponse(response)
                    if (response != null) {
                        uploadVideoSuccess(poster, url, response)
                    }
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    uploadVideoFailure()
                }
            })
    }

    private fun uploadVideoSuccess(poster: String, url: String, data: JsonObject) {
        processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
        currentUploadingVideo?.let {
            mUploadVideoListener?.changePoster(it.id, poster)
            mUploadVideoListener?.videoUploadFinished(it.id, url, data)
            UploadManager.cancelTask(it.filePath)
            localVideoList.remove(it)
        }
        currentUploadingVideo = null
        uploadVideo()
    }

    private fun uploadVideoFailure() {
        processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
        currentUploadingVideo?.let {
            runOnUiThread {
                mUploadVideoListener?.videoUploadFailed(it.id)
            }
            uploadVideoErrorList.add(it)
            localVideoList.remove(it)
            UploadManager.cancelTask(it.filePath)
        }
        currentUploadingVideo = null
        uploadVideo()
    }

    fun checkIsAllUploadedAndToast(): Boolean {
        if (localVideoList.isNotEmpty() || uploadVideoErrorList.isNotEmpty()) {
            ToastUtils.showToast("视频未上传完成，视频内容保存失败")
            return false
        }
        return true
    }

    private fun getVideoType(): String {
        return when (type) {
            BbsType.GAME_BBS.value -> {
                when (getRichType()) {
                    RichType.ARTICLE -> BbsType.GAME_BBS_ARTICLE_INSERT.value
                    RichType.QUESTION -> BbsType.GAME_BBS_QUESTION_INSERT.value
                    else -> ""
                }
            }
            BbsType.OFFICIAL_BBS.value -> {
                when (getRichType()) {
                    RichType.ARTICLE -> BbsType.OFFICIAL_BBS_ARTICLE_INSERT.value
                    RichType.QUESTION -> BbsType.OFFICIAL_BBS_QUESTION_INSERT.value
                    else -> ""
                }
            }
            else -> ""
        }
    }

    abstract fun getRichType(): RichType
}

interface UploadVideoListener {
    /**
     * 插入视频占位图
     */
    fun insertPlaceholderVideo(id: String, poster: String)

    /**
     * 更新视频进度条
     */
    fun updateVideoProgress(id: String, progress: String)

    /**
     * 上传视频完成
     */
    fun videoUploadFinished(id: String, url: String, msg: JsonObject)

    /**
     * 更换封面图
     */
    fun changePoster(id: String, poster: String)

    /**
     * 上传失败
     */
    fun videoUploadFailed(id: String)
}

enum class RichType {
    ARTICLE,
    QUESTION,
    ANSWER
}