package com.yidian.common.utils

import android.content.Context
import android.util.Log
import com.google.gson.Gson
import com.ksyun.ks3.exception.Ks3Error
import com.ksyun.ks3.model.acl.CannedAccessControlList
import com.ksyun.ks3.services.AuthListener
import com.ksyun.ks3.services.Ks3Client
import com.ksyun.ks3.services.Ks3ClientConfiguration
import com.ksyun.ks3.services.handler.PutObjectACLResponseHandler
import com.ksyun.ks3.services.handler.PutObjectResponseHandler
import com.ksyun.ks3.services.request.PutObjectRequest
import com.yidian.common.YdBaseApplication
import com.yidian.common.http.CommonDataSource
import com.yidian.common.http.bean.GetKSYunObjectIdBean
import com.yidian.common.http.callback.IGetKSYunObjectIdCallback
import com.yidian.utils.MD5Util
import com.yidian.yac.ftdevicefinger.core.FtDeviceFingerManager
import cz.msebera.android.httpclient.Header
import java.io.File

/**
 * author: yinjiacheng
 * date: 5/20/21 11:08 AM
 * description: 金山云存储
 */
class KS3Core private constructor(val context: Context) : AuthListener {

  private val client by lazy { Ks3Client(this, context) }
  private val gson by lazy { Gson() }
  private val putObjectResponseHandler by lazy {
    object : PutObjectResponseHandler() {
      override fun onTaskProgress(progress: Double) {
        // work thread
      }

      override fun onTaskFailure(
        statesCode: Int,
        error: Ks3Error?,
        responceHeaders: Array<out Header>?,
        response: String?,
        throwable: Throwable?
      ) {
        Log.e(
          TAG,
          "put object fail:, errorMsg: ${error?.errorMessage}"
        )
      }

      override fun onTaskSuccess(statesCode: Int, responceHeaders: Array<out Header>?) {
        Log.d(TAG, "put object success.")
      }

      override fun onTaskStart() {

      }

      override fun onTaskFinish() {

      }

      override fun onTaskCancel() {

      }
    }
  }

  private val putObjectACLResponseHandler by lazy {
    object : PutObjectACLResponseHandler() {
      override fun onSuccess(statesCode: Int, responceHeaders: Array<out Header>?) {
        Log.d(TAG, "put object acl success.")
      }

      override fun onFailure(
        statesCode: Int,
        error: Ks3Error?,
        responceHeaders: Array<out Header>?,
        response: String?,
        throwable: Throwable?
      ) {
        Log.e(TAG, "put object acl fail:, errorMsg: ${error?.errorMessage}")
      }
    }
  }

  companion object {
    const val TAG = "KS3Core"
    const val DEFAULT_BUCKET_NAME = "bp-yidian"
    const val DEFAULT_END_POINT = "ks3-cn-beijing.ksyun.com"
    const val VIDEO_SUFFIX = ".mp4"
    const val IMAGE_SUFFIX = ".jpg"
    val INSTANCE: KS3Core by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
      KS3Core(context = YdBaseApplication.context)
    }
  }

  init {
    setupClient()
  }

  /**
   * 配置金山云client
   */
  private fun setupClient() {
    client.setConfiguration(Ks3ClientConfiguration.getDefaultConfiguration())
    client.setEndpoint(DEFAULT_END_POINT)
  }

  /**
   * 文件上传
   * @param path 本地文件全路径
   * @param type 文件类型
   * @param scenario 业务场景
   */
  fun uploadObject(
    path: String,
    type: ObjectType,
    scenario: ScenarioType,
    listener: OnKS3TaskListener
  ) {
    CommonDataSource.getKSYunObjectId(object : IGetKSYunObjectIdCallback {
      override fun getKSYunObjectIdSuccess(result: GetKSYunObjectIdBean.Response?) {
        Log.d(
          TAG,
          "request getKSYun objectId success, objectId: ${result?.objectId}, bucket: ${result?.bucket}"
        )
        // 执行金山云上传
        val request = PutObjectRequest(
          result?.bucket,
          result?.objectId + getObjectKeySuffix(type),
          File(path)
        )
        request.cannedAcl = CannedAccessControlList.Private
        client.putObject(
          request,
          KS3CorePutObjectResponseHandler(
            result?.bucket,
            result?.objectId + getObjectKeySuffix(type),
            listener
          )
        )
      }

      override fun getKSYunObjectIdFailure(message: String?) {
        Log.e(TAG, "request get KSYun objectId fail, message: $message")
      }
    }, hashMapOf(Pair("scenario", scenario.scenario)))
  }

  /**
   * 文件上传
   * @param path 本地文件全路径
   * @param scenario 业务场景
   */
  fun uploadObject(path: String, scenario: ScenarioType, listener: OnKS3TaskListener) {
    CommonDataSource.getKSYunObjectId(object : IGetKSYunObjectIdCallback {
      override fun getKSYunObjectIdSuccess(result: GetKSYunObjectIdBean.Response?) {
        Log.d(
          TAG,
          "request getKSYun objectId success, objectId: ${result?.objectId}, bucket: ${result?.bucket}"
        )
        // 执行金山云上传
        val request = PutObjectRequest(result?.bucket, result?.objectId, File(path))
        request.cannedAcl = CannedAccessControlList.Private
        client.putObject(
          request,
          KS3CorePutObjectResponseHandler(result?.bucket, result?.objectId, listener)
        )
      }

      override fun getKSYunObjectIdFailure(message: String?) {
        Log.e(TAG, "request get KSYun objectId fail, message: $message")
      }
    }, hashMapOf(Pair("scenario", scenario.scenario)))
  }

  /**
   * 文件上传
   * @param file 文件实体
   * @param scenario 业务场景
   */
  fun uploadObject(file: File, scenario: ScenarioType, listener: OnKS3TaskListener) {
    CommonDataSource.getKSYunObjectId(object : IGetKSYunObjectIdCallback {
      override fun getKSYunObjectIdSuccess(result: GetKSYunObjectIdBean.Response?) {
        Log.d(
          TAG,
          "request getKSYun objectId success, objectId: ${result?.objectId}, bucket: ${result?.bucket}"
        )
        // 执行金山云上传
        val request = PutObjectRequest(result?.bucket, result?.objectId, file)
        request.cannedAcl = CannedAccessControlList.Private
        client.putObject(
          request,
          KS3CorePutObjectResponseHandler(result?.bucket, result?.objectId, listener)
        )
      }

      override fun getKSYunObjectIdFailure(message: String?) {
        Log.e(TAG, "request get KSYun objectId fail, message: $message")
      }
    }, hashMapOf(Pair("scenario", scenario.scenario)))
  }

  /**
   * 文件上传
   * @param file 文件实体
   * @param type 文件类型
   * @param scenario 业务场景
   */
  fun uploadObject(
    file: File,
    type: ObjectType,
    scenario: ScenarioType,
    listener: OnKS3TaskListener
  ) {
    CommonDataSource.getKSYunObjectId(object : IGetKSYunObjectIdCallback {
      override fun getKSYunObjectIdSuccess(result: GetKSYunObjectIdBean.Response?) {
        Log.d(
          TAG,
          "request getKSYun objectId success, objectId: ${result?.objectId}, bucket: ${result?.bucket}"
        )
        // 执行金山云上传
        val request = PutObjectRequest(
          result?.bucket,
          result?.objectId + getObjectKeySuffix(type),
          file
        )
        request.cannedAcl = CannedAccessControlList.Private
        client.putObject(
          request,
          KS3CorePutObjectResponseHandler(
            result?.bucket,
            result?.objectId + getObjectKeySuffix(type),
            listener
          )
        )
      }

      override fun getKSYunObjectIdFailure(message: String?) {
        Log.e(TAG, "request get KSYun objectId fail, message: $message")
      }
    }, hashMapOf(Pair("scenario", scenario.scenario)))
  }

  /**
   * 获取ObjectKey后缀
   */
  private fun getObjectKeySuffix(type: ObjectType): String {
    return when (type) {
      ObjectType.IMAGE -> IMAGE_SUFFIX
      ObjectType.VIDEO -> VIDEO_SUFFIX
    }
  }

  /**
   * 本地生成ObjectKey
   * 作为Object在云空间的唯一标识
   */
  private fun getLocalObjectKey(name: String, type: ObjectType): String {
    return MD5Util.md5Encrypt32Upper(name + FtDeviceFingerManager.getDeviceFinger()) +
      if (type == ObjectType.VIDEO) VIDEO_SUFFIX else IMAGE_SUFFIX
  }

  /**
   * 调用金山云API后 SDK会回调此函数用于获取必要参数向AppServer请求token
   */
  override fun onCalculateAuth(
    httpMethod: String,
    contentType: String,
    date: String,
    contentMD5: String,
    resource: String,
    headers: String
  ): String? {
    // 此处由APP端向业务服务器发送post请求返回Token
    // 该回调方法运行在非主线程
    Log.d(
      TAG,
      "KSYun calculate auth, httpMethod: $httpMethod, contentType: $contentType, date: $date, contentMD5: $contentMD5, resource: $resource, headers: $headers"
    )
    val requestParams = HashMap<String, String?>()
    requestParams["http_method"] = httpMethod
    requestParams["date"] = date
    requestParams["resource"] = resource
    requestParams["content_md5"] = contentMD5
    requestParams["content_type"] = contentType
    requestParams["headers"] = headers
    return CommonDataSource.getKSYunToken(requestParams)?.result?.token
  }

  /**
   * 业务监听
   */
  interface OnKS3TaskListener {
    fun onTaskStart()
    fun onTaskProgress(progress: Double)
    fun onTaskFinish()
    fun onTaskCancel()
    fun onTaskSuccess(bucket: String?, objectKey: String?)
    fun onTaskFailure(statesCode: Int, message: String?)
  }

  /**
   * 上传对象的类型
   */
  enum class ObjectType {
    VIDEO, IMAGE
  }

  /**
   * 业务场景的类型
   */
  enum class ScenarioType(val scenario: String) {
    // 身份证
    ID_CARD("id_card"),

    // 活体数据
    ALIVE_DETECT("living_data"),

    // 营业执照
    BUSINESS_LICENSE("business_license"),

    // 商品
    COMMODITY("goods")
  }

  /**
   * 金山云object上传handler
   */
  class KS3CorePutObjectResponseHandler(
    private val bucket: String?,
    private val objectKey: String?,
    val listener: OnKS3TaskListener
  ) : PutObjectResponseHandler() {

    override fun onTaskProgress(progress: Double) {
      // work thread
      listener.onTaskProgress(progress)
    }

    override fun onTaskFailure(
      statesCode: Int,
      error: Ks3Error?,
      responceHeaders: Array<out Header>?,
      response: String?,
      throwable: Throwable?
    ) {
      Log.e(
        TAG,
        "put object fail:, errorMsg: ${error?.errorMessage}"
      )
      listener.onTaskFailure(statesCode, error?.errorMessage)
    }

    override fun onTaskSuccess(statesCode: Int, responceHeaders: Array<out Header>?) {
      Log.d(TAG, "put object success.")
      listener.onTaskSuccess(bucket, objectKey)
    }

    override fun onTaskStart() {
      listener.onTaskStart()
    }

    override fun onTaskFinish() {
      listener.onTaskFinish()
    }

    override fun onTaskCancel() {
      listener.onTaskCancel()
    }
  }

}
