package com.gh.gamecenter.qa.comment.base

import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.ErrorHelper
import com.gh.common.util.NewLogUtils
import com.gh.common.util.PostCommentUtils
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.baselist.LoadParams
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.json.json
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.syncpage.SyncDataEntity
import com.gh.gamecenter.common.syncpage.SyncFieldConstants
import com.gh.gamecenter.common.syncpage.SyncPageRepository
import com.gh.gamecenter.common.utils.observableToMain
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.entity.CommentDraft
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.entity.MeEntity
import com.gh.gamecenter.feature.entity.Permissions
import com.gh.gamecenter.qa.article.detail.CommentItemData
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
import com.gh.gamecenter.room.AppDatabase
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONObject
import retrofit2.HttpException

abstract class BaseCommentViewModel(
    application: Application,
    var articleId: String,
    var videoId: String,
    var questionId: String,
    var communityId: String,
    var gameCollectionId: String,
    var topCommentId: String = ""
) : ListViewModel<CommentEntity, CommentItemData>(application) {
    protected val mApi: ApiService = RetrofitManager.getInstance().api
    protected var mTotal: Int = 0
    var currentSortType: SortType = SortType.OLDEST

    val loadResultLiveData = MutableLiveData<LoadResult>()

    var topItemData: CommentItemData? = null

    var commentCount = 0

    var isHandleTopComment = false

    override fun loadStatusControl(size: Int) {
        if (mCurLoadParams.loadOffset == LoadParams.DEFAULT_OFFSET) { // 初始化列表
            when {
                size == 0 -> {
                    mLoadStatusLiveData.setValue(LoadStatus.INIT_EMPTY)
                }
                size == REQUEST_FAILURE_SIZE -> {
                    // TODO 处理列表加载失败问题
                    mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED)
                }
                size < mOverLimitSize -> {
                    // 避免一个屏幕出现两次分页
                    mLoadStatusLiveData.setValue(LoadStatus.INIT_OVER)
                }
                else -> mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED)
            }
        } else {
            when (size) {
                REQUEST_FAILURE_SIZE -> mLoadStatusLiveData.setValue(LoadStatus.LIST_FAILED)
                0 -> mLoadStatusLiveData.setValue(LoadStatus.LIST_OVER)
                else -> mLoadStatusLiveData.setValue(LoadStatus.LIST_LOADED)
            }
        }

        if (size == REQUEST_FAILURE_SIZE) {
            mRetryParams = mCurLoadParams
        } else {
            mRetryParams = null
            mCurLoadParams.loadOffset = mCurLoadParams.loadOffset + 1 // 页数 + 1
        }
    }

    open fun changeSort(sortType: SortType) {
        if (sortType != currentSortType) {
            currentSortType = sortType
            load(LoadType.REFRESH)
            NewLogUtils.logCommentClick(if (sortType == SortType.LATEST) "click_positive_sequence" else "click_reverse_order")
        }
    }

    open fun mergeListData(
        commentList: List<CommentEntity>?,
        hasFilter: Boolean = true,
    ) {
        topItemData?.let {
            val mergedList = arrayListOf<CommentItemData>().apply {
                if (hasFilter) {
                    if (mResultLiveData.value?.firstOrNull() != null) {
                        // 保持头两个 item 的内存地址不变
                        add(mResultLiveData.value!![0])
                        add(mResultLiveData.value!![1])
                    } else {
                        add(topItemData!!)
                        add(CommentItemData(filter = true))
                    }
                } else {
                    add(topItemData!!)
                }
                if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_EMPTY) {
                    add(CommentItemData(errorEmpty = true))
                } else if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_FAILED) {
                    add(CommentItemData(errorConnection = true))
                } else {
                    commentList?.forEachIndexed { index, entity ->
                        // 没有 me 会导致不能跨页面更新点赞
                        if (entity.me == null) {
                            entity.me = MeEntity()
                        }
                        handleTopComment(index, entity)
                        add(CommentItemData(commentNormal = entity))
                    }
                    add(CommentItemData(footer = true))
                }
            }
            mResultLiveData.postValue(mergedList)
        }
    }

    @SuppressLint("CheckResult")
    fun getSingleCommentDetail(commentId: String) {
        val itemDataList = mResultLiveData?.value ?: return
        val count = itemDataList.count { it.commentNormal != null }
        if (count == 0) {
            load(LoadType.REFRESH)
            return
        }
        val single = when {
            articleId.isNotEmpty() -> {
                mApi.getCommunityArticleComment(commentId)
            }
            videoId.isNotEmpty() -> {
                mApi.getCommunityVideoComment(commentId)
            }
            questionId.isNotEmpty() -> {
                mApi.getCommunityQuestionComment(questionId, commentId)
            }
            gameCollectionId.isNotEmpty() -> {
                mApi.getGameCollectionComment(gameCollectionId, commentId)
            }
            else -> null
        } ?: return
        single.compose(singleToMain())
            .subscribe(object : BiResponse<CommentEntity>() {
                override fun onSuccess(data: CommentEntity) {
                    when (currentSortType) {
                        SortType.LATEST -> {
                            //插入到第一个
                            val index = itemDataList.indexOfFirst { it.commentNormal != null }
                            if (index >= 0) {
                                mTotal++
                                itemDataList.add(index, CommentItemData(commentNormal = data))
                                mResultLiveData.postValue(itemDataList)
                            } else {
                                load(LoadType.REFRESH)
                            }
                        }
                        SortType.OLDEST -> {
                            //根据total判断是否插入到最后
                            if (count >= mTotal) {
                                mTotal++
                                val index = itemDataList.indexOfLast { it.commentNormal != null }
                                itemDataList.add(index + 1, CommentItemData(commentNormal = data))
                                mResultLiveData.postValue(itemDataList)
                            } else {
                                load(LoadType.REFRESH)
                            }
                        }
                    }
                }
                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    load(LoadType.REFRESH)
                }
            })
    }

    fun handleTopComment(index: Int, entity: CommentEntity) {
        if (getHandleTopCommentCondition(index)) {
            isHandleTopComment = true
            entity.isHighlight = true
            entity.isForceExpand = true
        }
    }

    open fun getHandleTopCommentCondition(index: Int) =
        !isHandleTopComment && topItemData != null && topCommentId.isNotBlank() && index == 0

    fun getCommentDraft(id: String): CommentDraft {
        return AppDatabase.getInstance().commentDraftDao().getDraftById(id)
    }

    fun getLikeText(likeCount: Int, defaultLikeText: String = "赞同"): String {
        return if (likeCount == 0) defaultLikeText else "$likeCount"
    }

    fun getCommentText(commentCount: Int, defaultComment: String): String {
        return if (commentCount == 0) defaultComment else "$commentCount"
    }

    fun like(comment: CommentEntity) {
        PostCommentUtils.likeComment(
            null,
            articleId,
            videoId,
            questionId,
            comment.id,
            object : PostCommentUtils.PostCommentListener {
                override fun postSuccess(response: JSONObject?) {
                    val cloneComment = comment.clone()
                    if (cloneComment.me == null) {
                        cloneComment.me = MeEntity()
                    }
                    cloneComment.me?.isCommentVoted = true
                    cloneComment.vote++
                    updateComment(cloneComment)

                    comment.id?.let {
                        SyncPageRepository.postSyncData(
                            SyncDataEntity(
                                it,
                                SyncFieldConstants.ARTICLE_COMMENT_VOTE_COUNT,
                                comment.vote + 1
                            )
                        )
                        SyncPageRepository.postSyncData(
                            SyncDataEntity(
                                it,
                                SyncFieldConstants.ARTICLE_COMMENT_VOTE,
                                true,
                                checkFieldEntity = true
                            )
                        )
                    }
                }

                override fun postFailed(e: Throwable) {
                    if (e is HttpException) {
                        if (e.code() == 403) {
                            try {
                                val detail = JSONObject(
                                    e.response().errorBody()!!.string()
                                ).getString("detail")
                                if ("voted" == detail) {
                                    ToastUtils.showToast("已经点过赞啦！")
                                }
                            } catch (ex: Exception) {
                                ex.printStackTrace()
                            }
                            return
                        }
                    }
                    Utils.toast(getApplication(), "网络异常，点赞失败")
                }
            })
    }

    fun unLike(comment: CommentEntity) {
        PostCommentUtils.unLikeComment(articleId, communityId, videoId, questionId, comment.id,
            object : PostCommentUtils.PostCommentListener {
                override fun postSuccess(response: JSONObject?) {
                    val cloneComment = comment.clone()
                    if (cloneComment.me == null) {
                        cloneComment.me = MeEntity()
                    }
                    cloneComment.me?.isCommentVoted = false
                    cloneComment.vote--
                    updateComment(cloneComment)

                    comment.id?.let {
                        SyncPageRepository.postSyncData(
                            SyncDataEntity(
                                it,
                                SyncFieldConstants.ARTICLE_COMMENT_VOTE_COUNT,
                                comment.vote - 1
                            )
                        )
                        SyncPageRepository.postSyncData(
                            SyncDataEntity(
                                it,
                                SyncFieldConstants.ARTICLE_COMMENT_VOTE,
                                false,
                                checkFieldEntity = true
                            )
                        )
                    }
                }

                override fun postFailed(error: Throwable?) {
                    Utils.toast(getApplication(), "网络异常，取消点赞失败")
                }
            })
    }

    @SuppressLint("CheckResult")
    open fun deleteComment(entity: CommentEntity, callback: () -> Unit) {
        val observable = when {
            videoId.isNotEmpty() -> {
                if (entity.me?.isModerator == true) {
                    mApi.moderatorsHideVideoComment(communityId, videoId, entity.id).toObservable()
                } else {
                    mApi.deleteVideoComment(entity.id).toObservable()
                }
            }
            questionId.isNotEmpty() -> {
                mApi.deleteQuestionComment(questionId, entity.id).toObservable()
            }
            articleId.isNotEmpty() -> {
                mApi.hideCommunityArticleComment(entity.id)
            }
            else -> null
        } ?: return
        observable.compose(observableToMain())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    mTotal--
                    hideCommentSuccess()
                    callback.invoke()
                    val indexOfFirst = mResultLiveData.value?.indexOfFirst { it.commentNormal?.id == entity.id } ?: -1
                    if (indexOfFirst >= 0) {
                        mResultLiveData.value?.removeAt(indexOfFirst)
                    }
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    e?.let { httpException ->
                        if (httpException.code() == 403) {
                            val string = e.response().errorBody()?.string() ?: ""
                            val errorJson = JSONObject(string)
                            val errorCode = errorJson.getInt("code")
                            if (errorCode == 403059) {
                                Utils.toast(getApplication(), "权限错误，请刷新后重试")
                                return
                            } else {
                                Utils.toast(getApplication(), e.message())
                            }
                        }
                    }
                }
            })
    }

    fun updateCommentTop(
        commentId: String,
        top: Boolean = false,
        isAgain: Boolean = false,
        callback: (isSuccess: Boolean, errorCode: Int) -> Unit
    ) {
        val observable = if (top) {
            val map = hashMapOf<String, Any>()
            if (isAgain) {
                map["again"] = isAgain
            }
            when {
                articleId.isNotEmpty() -> {
                    mApi.postArticleCommentTop(commentId, map)
                }
                questionId.isNotEmpty() -> {
                    mApi.postQuestionCommentTop(questionId, commentId, map)
                }
                videoId.isNotEmpty() -> {
                    mApi.postVideoCommentTop(commentId, map)
                }
                else -> null
            }
        } else {
            when {
                articleId.isNotEmpty() -> {
                    mApi.postArticleCommentUnTop(commentId)
                }
                questionId.isNotEmpty() -> {
                    mApi.postQuestionCommentUnTop(questionId, commentId)
                }
                videoId.isNotEmpty() -> {
                    mApi.postVideoCommentUnTop(commentId)
                }
                else -> null
            }
        } ?: return

        observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)
                    callback.invoke(true, 0)
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    e?.let {
                        ErrorHelper.handleErrorWithCustomizedHandler(
                            getApplication(),
                            e.response()?.errorBody()?.string(),
                            false
                        ) {
                            if (it == 403095) {
                                callback.invoke(false, it)
                                true
                            } else false
                        }
                    }
                }
            })
    }

    open fun onUpdateCommentTopSuccess() {
        load(LoadType.REFRESH)
    }

    fun acceptQuestionComment(questionId: String, comment: CommentEntity, accept: Boolean) {
        val body = json { "accept" to accept }.toRequestBody()
        mApi.acceptQuestionComment(questionId, comment.id, body)
            .compose(observableToMain())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)
                    val cloneComment = comment.clone()
                    cloneComment.accept = accept
                    updateComment(cloneComment)
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    ToastUtils.showToast("采纳失败")
                }
            })
    }

    fun highlightQuestionComment(comment: CommentEntity) {
        mApi.highlightAnswer(comment.id)
            .compose(observableToMain())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)
                    if ((comment.me?.moderatorPermissions?.highlightAnswer
                            ?: Permissions.GUEST) == Permissions.ADMIN
                    ) {
                        val cloneComment = comment.clone()
                        cloneComment.choiceness = true
                        updateComment(cloneComment)
                        ToastUtils.showToast("操作成功")
                    } else {
                        ToastUtils.showToast("提交成功")
                    }
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    ToastUtils.showToast("加精选失败")
                }
            })
    }

    fun chancelHighlightQuestionComment(comment: CommentEntity) {
        mApi.cancelHighlightAnswer(comment.id)
            .compose(observableToMain())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)
                    if ((comment.me?.moderatorPermissions?.cancelChoicenessAnswer
                            ?: Permissions.GUEST) == Permissions.ADMIN
                    ) {
                        val cloneComment = comment.clone()
                        cloneComment.choiceness = false
                        updateComment(cloneComment)
                        ToastUtils.showToast("操作成功")
                    } else {
                        ToastUtils.showToast("提交成功")
                    }
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    ToastUtils.showToast("取消精选失败")
                }
            })
    }

    open fun updateComment(cloneComment: CommentEntity) {
        mResultLiveData.value?.let {
            it.forEachIndexed { index, commentItemData ->
                commentItemData?.run {
                    when {
                        commentNormal?.id == cloneComment.id -> it[index] =
                            CommentItemData(commentNormal = cloneComment)
                        commentTop?.id == cloneComment.id -> it[index] = CommentItemData(commentTop = cloneComment)
                    }
                }
            }
            mResultLiveData.postValue(it)
        }
    }

    open fun hideCommentSuccess() {}

    enum class LoadResult {
        SUCCESS,
        NETWORK_ERROR,
        DELETED
    }

    enum class SortType(var value: String) {
        LATEST("time.create:-1"),
        OLDEST("time.create:1")
    }

}