package com.gh.gamecenter.video.upload

import android.os.Environment
import com.alibaba.sdk.android.oss.ClientConfiguration
import com.alibaba.sdk.android.oss.ClientException
import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.ServiceException
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask
import com.alibaba.sdk.android.oss.model.*
import com.gh.gamecenter.common.utils.NetworkUtils
import com.gh.gamecenter.common.entity.OssEntity
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import java.io.File

/**
 * todo BUG 上传过程中遇到网络问题中断 onFailure()方法无法响应
 * https://github.com/aliyun/aliyun-oss-android-sdk/issues/75
 */
class UploadThread(
    private val mOssEntity: OssEntity,
    private val mUploadListener: OnUploadListener
) : Thread() {

    private var mOssAsyncTask: OSSAsyncTask<ResumableUploadResult>? = null

    private var mLastSize: Long = 0
    private var mLastTime: Long = 0

    private var mNeedInvokeFailureInCompleted = true

    private val mMaxRetryCount = 3
    private var mRetryCount: Int = 0

    override fun run() {
        super.run()

        val conf = ClientConfiguration()
        conf.connectionTimeout = 15 * 1000
        conf.socketTimeout = 15 * 1000
        conf.maxConcurrentRequest = 5
        conf.maxErrorRetry = 2

        val credentialProvider =
            OSSStsTokenCredentialProvider(mOssEntity.accessKeyId, mOssEntity.accessKeySecret, mOssEntity.securityToken)
        val oss = OSSClient(HaloApp.getInstance().application, mOssEntity.endPoint, credentialProvider, conf)

        val recordDirectory = Environment.getExternalStorageDirectory().absolutePath + "/oss_record/"
        val recordDir = File(recordDirectory)
        if (!recordDir.exists()) {
            recordDir.mkdirs()
        }

        val request =
            ResumableUploadRequest(mOssEntity.bucket, mOssEntity.key, mOssEntity.uploadFilePath, recordDirectory)
        request.setDeleteUploadOnCancelling(false)
        request.progressCallback = OSSProgressCallback<ResumableUploadRequest> { _,
                                                                                 currentSize,
                                                                                 totalSize ->
            mUploadListener.onProgressChanged(
                mOssEntity.uploadFilePath,
                currentSize / 1000,
                totalSize / 1000,
                getSpeed(currentSize / 1000)
            )
        }

        mOssAsyncTask = oss.asyncResumableUpload(
            request,
            object : OSSCompletedCallback<ResumableUploadRequest, ResumableUploadResult> {
                override fun onSuccess(request: ResumableUploadRequest, result: ResumableUploadResult) {
                    mRetryCount = 0
                    mNeedInvokeFailureInCompleted = false
                    mUploadListener.onUploadSuccess(mOssEntity.uploadFilePath, mOssEntity.domain + mOssEntity.key)

                }

                override fun onFailure(
                    request: ResumableUploadRequest,
                    clientExcepion: ClientException,
                    serviceException: ServiceException
                ) {
                    mNeedInvokeFailureInCompleted = false
                    mUploadListener.onUploadFailure(
                        mOssEntity.uploadFilePath,
                        clientExcepion.message + "/" + serviceException.message
                    )
                }
            })

        mOssAsyncTask?.waitUntilFinished()

        Utils.log(UploadThread::class.java.simpleName + "=>upload task completed::" + mNeedInvokeFailureInCompleted)
        if (mNeedInvokeFailureInCompleted) {
            if (File(mOssEntity.uploadFilePath).exists() &&
                NetworkUtils.isNetworkConnected(HaloApp.getInstance().application)
            ) {
                if (mRetryCount <= mMaxRetryCount) {
                    sleep(2000)
                    run()
                }
                mRetryCount++
            } else {
                mUploadListener.onUploadFailure(mOssEntity.uploadFilePath, "not error body")
            }
        }
    }

    // kb/s
    private fun getSpeed(currentSize: Long): Long {
        return if (mLastTime != 0L) {
            val time = System.currentTimeMillis() - mLastTime
            val size = currentSize - mLastSize
            size / (time / 1000L)
        } else {
            mLastTime = System.currentTimeMillis()
            mLastSize = currentSize
            0
        }
    }

    fun cancel() {
        mNeedInvokeFailureInCompleted = false
        mOssAsyncTask?.cancel()
    }
}