package com.remax.visualnovel.app.viewmodel.base

import android.graphics.BitmapFactory
import com.amazonaws.auth.BasicSessionCredentials
import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener
import com.amazonaws.mobileconnectors.s3.transferutility.TransferState
import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility
import com.amazonaws.services.s3.AmazonS3Client
import com.amazonaws.services.s3.S3ClientOptions
import com.amazonaws.services.s3.model.ObjectMetadata
import com.remax.visualnovel.R
import com.remax.visualnovel.app.base.app.CommonApplicationProxy
import com.remax.visualnovel.constant.StatusCode
import com.remax.visualnovel.entity.request.ImgCheckDTO
import com.remax.visualnovel.entity.response.BucketBean
import com.remax.visualnovel.entity.response.base.ApiFailedResponse
import com.remax.visualnovel.entity.response.base.ApiSuccessResponse
import com.remax.visualnovel.entity.response.base.Response
import com.remax.visualnovel.extension.resumeWithActive
import com.remax.visualnovel.repository.api.OssRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.suspendCancellableCoroutine
import timber.log.Timber
import java.io.File
import javax.inject.Inject

/**
 * Created by HJW on 2022/11/9
 *
 * oss上传相关
 */
@HiltViewModel
open class OssViewModel @Inject constructor() : UserViewModel() {

    @Inject
    lateinit var ossRepository: OssRepository

    data class LoadFileData(
        var isSuccess: Boolean,
        val isViolation: Boolean = false,
        val errorMsg: String = "",
        var urlPath: String = "",
        var filePath: String = "",
        var width: Int = 0,
        var height: Int = 0
    )

    data class FileUpLoadRes(
        val loadFileData: LoadFileData,
        val fileOption: FileOption? = null,
    )

    data class FileOption(
        val path: String,
        val urlPath: String,
        val ossType: String,
        val width: Int,
        val height: Int
    )

    /**
     * 请求oss的token
     */
    suspend fun getBucketToken(postfix: String, ossType: String): Response<BucketBean> {
        return ossRepository.getS3Bucket(ossType, postfix)
    }

    /**
     * 挂起函数上传图片
     * @param filePath String
     * @param ossType String
     * @param isImg Boolean
     * @param checkNSFW Boolean 是否鉴黄
     * @param checkRealPerson Boolean 是否鉴定真人
     * @param checkKid Boolean 是否鉴定儿童
     * @return Response<LoadFileData> 封装成服务器返回一致类型处理
     */
    suspend fun ossUploadFile(
        filePath: String,
        ossType: String,
        isImg: Boolean = true,
        checkNSFW: Boolean = true,
        checkRealPerson: Boolean = false,
        checkKid: Boolean = false,
        token: BucketBean? = null
    ): Response<LoadFileData> {
        /**
         * 获取S3 STS Token对象
         */
        var s3BucketRes = token
        if (s3BucketRes == null) {
            val postfix = if (filePath.isNotEmpty()) filePath.substring(filePath.lastIndexOf(".") + 1) else "png"
            val getTokenRes = getBucketToken(postfix, ossType)
            //请求s3 token失败
            if (!getTokenRes.isApiSuccess) {
                return ApiFailedResponse(
                    errorMsg = getTokenRes.errorMsg,
                    errorData = createNormalErrorFileData(filePath).loadFileData
                )
            } else {
                s3BucketRes = getTokenRes.data!!
            }
        }
        val uploadRes = uploadFile(s3BucketRes, isImg, filePath, ossType)
        //上传图片失败
        if (!uploadRes.loadFileData.isSuccess) {
            return ApiFailedResponse(errorMsg = uploadRes.loadFileData.errorMsg, errorData = uploadRes.loadFileData)
        }
        //如果不是图片 或者 不需要鉴黄、鉴定真人、鉴定儿童，直接返回成功结果
        if (!isImg || (!checkNSFW && !checkRealPerson && !checkKid)) {
            return ApiSuccessResponse(uploadRes.loadFileData)
        }
        val fileOption = uploadRes.fileOption!!
        val checkDTO = ImgCheckDTO(fileOption.ossType, fileOption.path)
        //鉴黄
        val checkNSFWRes = if (checkNSFW) ossRepository.checkS3Img(checkDTO) else ApiSuccessResponse()
        return when {
            checkNSFWRes.isApiSuccess -> {
                ApiSuccessResponse(uploadRes.loadFileData)
            }

            else -> {
                ApiFailedResponse(StatusCode.UPLOAD_FILE_VIOLATION.code, checkNSFWRes.errorMsg, uploadRes.loadFileData)
            }
        }
    }

    /**
     * 包装上传失败的实体
     * @param filePath String 本地图片地址
     * @return FileUpLoadRes
     */
    private fun createNormalErrorFileData(filePath: String) = FileUpLoadRes(
        LoadFileData(
            isSuccess = false,
            errorMsg = CommonApplicationProxy.application.getString(R.string.upload_error),
            filePath = filePath
        )
    )

    /**
     * 协程处理亚马逊上传图片
     *
     * 使用带取消回调的协程，当上传回调时，需要判断协程isActive以防报错崩溃
     * @param stsToken BucketBean 授权信息
     * @param isImg Boolean 是否是图片
     * @param filePath String   本地地址
     * @param ossType String    上传类型
     * @return FileUpLoadRes    返回结果封装
     */
    private suspend fun uploadFile(
        stsToken: BucketBean,
        isImg: Boolean,
        filePath: String,
        ossType: String,
    ) = suspendCancellableCoroutine {
        it.invokeOnCancellation { _ ->
            it.resumeWithActive(createNormalErrorFileData(filePath))
        }

        val awsCreds = BasicSessionCredentials(
            stsToken.accessKeyId,
            stsToken.accessKeySecret,
            stsToken.securityToken
        )
        val uploadClient = AmazonS3Client(
            awsCreds,
            com.amazonaws.regions.Region.getRegion(stsToken.region)
        ).apply {
            setS3ClientOptions(
                S3ClientOptions.builder()
                    .setAccelerateModeEnabled(false)
                    .build()
            )
        }
        val transferUtility = TransferUtility.builder()
            .s3Client(uploadClient)
            .context(CommonApplicationProxy.application)
            .build()

        val fileName = filePath.substring(filePath.lastIndexOf("/") + 1)
        val path = if (stsToken.path.endsWith("*")) {
            stsToken.path.replace("*", fileName)
        } else {
            stsToken.path
        }
        val urlPath = if (stsToken.urlPath.endsWith("*")) {
            stsToken.urlPath.replace("*", fileName)
        } else {
            stsToken.urlPath
        }
        Timber.d("oss上传 - AmazonS3 Token  path:$path    urlPath：$urlPath")

        val obj = ObjectMetadata()
        obj.addUserMetadata("x-amz-tagging", "temp=1")

        val transferListener = object :
            TransferListener {
            override fun onStateChanged(id: Int, state: TransferState?) {
                Timber.d("oss上传 - AmazonS3 onStateChanged:$state")
                Timber.d("oss上传 - 协程状态 isActive: ${it.isActive} isCancelled: ${it.isCancelled} isCompleted: ${it.isCompleted}")
                when (state) {
                    TransferState.COMPLETED -> {
                        //此方法是上传图片完成后再打标签
//                        uploadClient.setObjectTagging(SetObjectTaggingRequest(stsToken.bucket, stsToken.path, ObjectTagging(listOf(Tag("temp", "1")))))
                        if (it.isActive) {
                            if (isImg) {
                                val options = BitmapFactory.Options()
                                options.inJustDecodeBounds = true
                                BitmapFactory.decodeFile(filePath, options)
                                val wid = options.outWidth
                                val hei = options.outHeight
                                val res = FileUpLoadRes(
                                    LoadFileData(
                                        isSuccess = true,
                                        urlPath = urlPath,
                                        width = wid,
                                        height = hei,
                                        filePath = filePath
                                    ),
                                    FileOption(path, urlPath, ossType, wid, hei)
                                )
                                it.resumeWithActive(res)
                            } else {
                                val res = FileUpLoadRes(
                                    LoadFileData(
                                        isSuccess = true,
                                        urlPath = urlPath,
                                        filePath = filePath
                                    )
                                )
                                it.resumeWithActive(res)
                            }
                        }
                    }

                    TransferState.FAILED, TransferState.CANCELED -> {
                        it.resumeWithActive(createNormalErrorFileData(filePath))
                    }

                    else -> {

                    }
                }
            }

            override fun onProgressChanged(id: Int, bytesCurrent: Long, bytesTotal: Long) {
                Timber.d("oss上传 - AmazonS3 onProgressChanged - bytesTotal:${bytesTotal} - bytesCurrent:${bytesCurrent}")
            }

            override fun onError(id: Int, ex: Exception?) {
                Timber.d("oss上传 - AmazonS3 onError:${ex?.localizedMessage} - id:$id")
            }
        }

        when {
            filePath.isNotEmpty() -> {
                val file = File(filePath)
                if (!file.exists()) {
                    it.cancel()
                }
                Timber.d("oss上传 - 上传文件大小 ${file.length() / 1024}")
                transferUtility.upload(stsToken.bucket, path, file, obj)
                    .setTransferListener(transferListener)
            }
        }
    }

    suspend fun checkText(content: String): Response<Any> = ossRepository.checkText(content)

}