package com.gh.gamecenter.common.utils

import com.gh.gamecenter.common.entity.OssEntity
import com.gh.gamecenter.common.retrofit.RetrofitManager
import com.lightgame.utils.Utils
import com.volcengine.tos.TOSClientConfiguration
import com.volcengine.tos.TOSV2ClientBuilder
import com.volcengine.tos.auth.StaticCredentials
import com.volcengine.tos.model.`object`.ObjectMetaRequestOptions
import com.volcengine.tos.model.`object`.PutObjectFromFileInput
import com.volcengine.tos.transport.TransportConfig
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.File


object OssUploadUtils {

    private var mProgressCallbackTimeInMills: Long = 0

    //上传文件类型
    enum class UploadType(val value: String) {
        GAME("game"),//后缀apk
        GAME_ARCHIVE("game_archive"),//游戏存档
        GAME_VIDEO("game_video"),
        VIDEO_UNPACKED("video_unpacked"),
        USER("user")
    }

    //获取Oss配置
    private fun getOssUpdateConfig(type: UploadType): Single<OssEntity> {
        return RetrofitManager.getInstance()
            .newApi
            .getOssUpdateConfig(type.value)
            .subscribeOn(Schedulers.io())

    }

    //异步上传文件
    @JvmStatic
    fun uploadFile(
        path: String,
        uploadType: UploadType,
        listener: OnUploadFileListener?,
        gameId: String = "",
        archiveMD5: String = "",
    ): Disposable {
        return getOssUpdateConfig(uploadType)
            .subscribeOn(Schedulers.io())
            .flatMap { mOssEntity ->
                Single.create<String?> {
                    val config = TransportConfig.builder()
                        .readTimeoutMills(15 * 1000)
                        .writeTimeoutMills(15 * 1000)
                        .connectTimeoutMills(15 * 1000)
                        .maxConnections(5)
                        .maxRetryCount(2)
                        .build()
                    val configuration = TOSClientConfiguration.builder()
                        .transportConfig(config)
                        .region(mOssEntity.region)
                        .endpoint(mOssEntity.endPoint)
                        .credentials(
                            StaticCredentials(mOssEntity.accessKey, mOssEntity.secretKey)
                                .withSecurityToken(mOssEntity.securityToken)
                        )
                        .build()
                    val tos = TOSV2ClientBuilder().build(configuration)
                    val file = File(path)
                    val options = ObjectMetaRequestOptions()
                        .setContentLength(file.length())
                    val key =
                        if (uploadType == UploadType.GAME_ARCHIVE) "${mOssEntity.key}${gameId}_${archiveMD5}.zip" else mOssEntity.key
                    // 构造上传请求
                    try {
                        val input = PutObjectFromFileInput.builder()
                            .file(file)
                            .bucket(mOssEntity.bucket)
                            .key(key)
                            .options(options)
                            .dataTransferListener { status ->
                                Utils.log(
                                    "OssUpload",
                                    "upload progress changed, consumedBytes -> ${status.consumedBytes}, totalBytes -> ${status.totalBytes}"
                                )
                                val currentTimeInMills = System.currentTimeMillis()
                                if (currentTimeInMills - mProgressCallbackTimeInMills > 200L) {
                                    listener?.onProgress(status.consumedBytes, status.totalBytes)
                                    mProgressCallbackTimeInMills = currentTimeInMills
                                }
                            }
                            .build()
                        tos.putObjectFromFile(input)
                        val url = "${mOssEntity.domain}$key"
                        Utils.log("OssUpload", "upload succeed, url -> $url")
                        it.onSuccess(url)
                    } catch (e: Throwable) {
                        Utils.log("OssUpload", "upload failed, exception -> $e")
                        it.onError(e)
                    }
                }
            }.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { url: String?, throwable ->
                if (!url.isNullOrBlank()) {
                    listener?.onSuccess(url)
                    return@subscribe
                }
                if (throwable != null) {
                    listener?.onError(throwable)
                }
            }
    }

    interface OnUploadFileListener {
        fun onSuccess(url: String)
        fun onProgress(consumeBytes: Long, totalBytes: Long) = Unit
        fun onError(e: Throwable?)
    }
}