package com.adealink.weparty.oss

import com.adealink.frame.base.CommonDataNullError
import com.adealink.frame.base.IError
import com.adealink.frame.base.Rlt
import com.adealink.frame.coroutine.dispatcher.Dispatcher
import com.adealink.frame.log.Log
import com.adealink.frame.oss.IOssService
import com.adealink.frame.oss.constant.TAG_OSS
import com.adealink.frame.oss.data.OSSDownloadError
import com.adealink.frame.oss.data.OSSUploadError
import com.adealink.frame.oss.data.OssResultData
import com.adealink.frame.oss.data.UploadFile
import com.adealink.frame.oss.data.UploadFile.LocalFile
import com.adealink.frame.oss.listener.IOssListener
import com.adealink.frame.util.deleteFiles
import com.adealink.weparty.App
import com.adealink.weparty.oss.data.OssUploadRes
import com.adealink.weparty.oss.data.toUploadReq
import com.adealink.weparty.oss.request.ProgressRequestBody
import com.adealink.weparty.oss.request.UploadProgressListener
import kotlinx.coroutines.withContext
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File

object OssService : IOssService {
    private val ossHttpService by lazy {
        App.instance.networkService.getHttpService(OssHttpService::class.java)
    }
    private val compressor = OssCompressor()
    override suspend fun suspendUploadFile(
        file: UploadFile,
        l: IOssListener?
    ): Rlt<String> {
        if (file is UploadFile.ByteFile) {
            return Rlt.Failed(OSSUploadError("Unsupported"))
        }
        val uploadFile = file as UploadFile.LocalFile

        var deleteCompressedFile = false
        var deleteAfterUploadedPath: String? = null
        var uploadSuccess = false
        if (uploadFile.deleteAfterUploaded) {
            deleteAfterUploadedPath = uploadFile.path
        }
        if (uploadFile.compress) {
            uploadFile.path = when {
                uploadFile.fileType.isImageCompressible() -> {
                    when (val rlt = compressor.compressImage(uploadFile.path)) {
                        is Rlt.Success -> {
                            deleteCompressedFile = uploadFile.path != rlt.data
                            rlt.data
                        }

                        is Rlt.Failed -> file.path
                    }
                }

                uploadFile.fileType.isVideoCompressible() -> {
                    when (val rlt = compressor.compressVideo(uploadFile.path)) {
                        is Rlt.Success -> {
                            deleteCompressedFile = uploadFile.path != rlt.data
                            rlt.data
                        }

                        is Rlt.Failed -> file.path
                    }
                }

                else -> {
                    file.path
                }
            }
        }

        val uploadResult: Rlt<String> = uploadFile(uploadFile, l)
        when (uploadResult) {
            is Rlt.Failed -> {

            }

            is Rlt.Success -> {
                uploadSuccess = true
            }
        }

        if (deleteCompressedFile) {
            withContext(Dispatcher.WENEXT_THREAD_POOL) {
                deleteFiles(uploadFile.path)
            }
        }

        if (uploadSuccess && !deleteAfterUploadedPath.isNullOrEmpty()) {
            withContext(Dispatcher.WENEXT_THREAD_POOL) {
                deleteFiles(deleteAfterUploadedPath)
            }
        }

        return uploadResult
    }


    private suspend fun uploadFile(
        file: LocalFile,
        l: IOssListener?
    ): Rlt<String> {
        return withContext(Dispatcher.WENEXT_THREAD_POOL) {
            val uploadUrlRlt = getUploadUrl(file)
            if (uploadUrlRlt is Rlt.Failed) {
                return@withContext uploadUrlRlt
            }

            val uploadOssRes = (uploadUrlRlt as Rlt.Success).data
            if (uploadOssRes.uploadUrl.isEmpty() || uploadOssRes.fileUrl.isEmpty()) {
                return@withContext Rlt.Failed(CommonDataNullError())
            }

            try {
                val uploadCall = App.instance.networkService.fileHttpClient
                    .newCall(
                        Request.Builder()
                            .url(uploadOssRes.uploadUrl)
                            .method(
                                ("put").uppercase(), ProgressRequestBody(
                                File(file.path).asRequestBody(),
                                object : UploadProgressListener {
                                    override fun onProgress(total: Long, current: Long) {
                                        l?.onProgress(null, current, total)
                                    }
                                }
                            ))
                            .build()
                    )
                val response = uploadCall.execute()
                if (response.isSuccessful) {
                    Rlt.Success(uploadOssRes.fileUrl)
                } else {
                    val result = response.body?.string()
                    Rlt.Failed(OSSUploadError(result ?: ""))
                }
            } catch (e: Exception) {
                Rlt.Failed(OSSUploadError(e.message ?: ""))
            }
        }
    }

    private suspend fun getUploadUrl(file: LocalFile): Rlt<OssUploadRes> {
        val rlt = ossHttpService.getOssUploadUrl(
            file.toUploadReq()
        )
        if (rlt is Rlt.Failed) {
            val errorCode = rlt.error.serverCode
            val errorMsg = rlt.error.msg
            Log.e(TAG_OSS, "getFederationToken, errorCode:${errorCode}, errorMsg:${errorMsg}")
            return Rlt.Failed(IError(msg = errorMsg, serverCode = errorCode))
        }

        val data = (rlt as Rlt.Success).data.data
        if (data == null) {
            return Rlt.Failed(CommonDataNullError())
        }
        return Rlt.Success(data)
    }

    override val bucket: String
        get() = ""

    override fun getUrlByPath(path: String): String {
        return ""
    }

    override suspend fun suspendDownloadFile(
        obj: String,
        filePath: String,
        l: IOssListener?
    ): Rlt<OssResultData> {
        return Rlt.Failed(OSSDownloadError("Unsupported"))
    }

}