package com.yidian.bcommon.services

import android.app.Activity
import android.net.Uri
import android.os.Build
import android.util.Base64
import com.google.gson.Gson
import com.luck.picture.lib.PictureSelector
import com.luck.picture.lib.config.PictureMimeType
import com.luck.picture.lib.entity.LocalMedia
import com.luck.picture.lib.listener.OnResultCallbackListener
import com.luck.picture.lib.manager.PictureCacheManager
import com.yidian.bcommon.bean.*
import com.yidian.bcommon.YdBaseApplication
import com.yidian.bcommon.utils.GlideEngine
import com.yidian.framework.mobile.insight.manage.XInsight
import com.yidian.framework.mobile.insight.storage.ImageUploadListener
import com.yidian.framework.mobile.insight.storage.bean.ImageUploadBean
import com.yidian.xarc.xrouter.ResultResolver
import com.yidian.xpage.observer.DStackActivityManager
import com.yidian.yac.core.zap.ZapService
import org.json.JSONArray
import org.json.JSONObject
import timber.log.Timber
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
import kotlin.collections.ArrayList

/**
 * author: yinjiacheng
 * date: 7/7/21 7:38 PM
 * description: 图片服务
 * 提供调用原生相机拍照、图片选择器、图片预览、图片上传能力
 */
// TODO: 7/9/21 动态获取权限
class ImageService : ZapService() {

  companion object {
    private const val TAG = "ImageService"
    const val SERVICE_NAME = "imageService"

    // 选择图片
    const val ACTION_SELECT_PIC = "selectImage"

    // 拍照
    const val ACTION_TAKE_PIC = "takePhoto"

    // 上传图片
    const val ACTION_UPLOAD_PIC = "uploadImage"

    // 预览图片 本期不做
    const val ACTION_PREVIEW_PIC = "previewPic"

    // 权限获取失败
    private const val CODE_PERMISSION_GRANT_FAIL = 20301

    // 选择失败、选择取消、拍照取消
    private const val CODE_ACTION_CANCEL = 20302

    // 文件不存在
    private const val CODE_FILE_NOT_EXIST = 20306

    // 文件上传失败
    private const val CODE_FILE_UPLOAD_FAIL = 20307

    // 图片最大选择数量
    private const val SELECT_PIC_MAX_COUNT = 9
  }

  private val gson by lazy { Gson() }

  /**
   * 当前栈顶Activity实例
   */
  private lateinit var curTopActivity: Activity

  /**
   * 缓存已选择图片
   */
  private val localSelectedImages by lazy { ArrayList<LocalSelectedImageBean>() }

  /**
   * 自定义压缩路径
   */
  private val picCompressPath by lazy { YdBaseApplication.context.externalCacheDir?.absolutePath + "/picture_selector_compress/" }

  /**
   * 自定义原图路径
   * 适配Android10
   */
  private val picOriginalPath by lazy { YdBaseApplication.context.externalCacheDir?.absolutePath + "/picture_selector_original/" }

  /**
   * 图片上传队列
   */
  private val uploadPicQueue by lazy { ConcurrentLinkedQueue<LocalUploadImageBean>() }

  /**
   * 准备上传的图片数量
   */
  private var uploadPicCount = 0

  /**
   * @param resolver 此参数建议作为具体能力的方法入参 避免使用全局变量（异步回传H5结果时seq顺序错乱）
   */
  override fun onAction(path: String, action: String, params: Any, options: Any, resolver: ResultResolver) {
    curTopActivity = DStackActivityManager.getInstance().topActivity
    val resolveParams = params as JSONObject
    when (action) {
      ACTION_SELECT_PIC -> {
        selectPic(resolver, gson.fromJson(resolveParams.toString(), ActionSelectImageParamsBean::class.java))
      }
      ACTION_TAKE_PIC -> {
        takePic(resolver)
      }
      ACTION_UPLOAD_PIC -> {
        uploadPic(resolver, resolveParams.optString("id"))
      }
      ACTION_PREVIEW_PIC -> {
        // 本期不做
      }
    }
  }

  override fun onExit() {
    PictureCacheManager.deleteAllCacheDirRefreshFile(YdBaseApplication.context)
  }

  override fun onInit() {
    File(picCompressPath).apply {
      if (!exists()) {
        mkdirs()
      }
    }
    File(picOriginalPath).apply {
      if (!exists()) {
        mkdirs()
      }
    }
  }

  /**
   * 选择图片
   * @param params selectImage操作业务参数
   * @param resolver 结果回传
   */
  private fun selectPic(resolver: ResultResolver, params: ActionSelectImageParamsBean) {
    PictureSelector.create(curTopActivity)
      .openGallery(PictureMimeType.ofImage())
      .imageEngine(GlideEngine.createGlideEngine())
      .maxSelectNum(params.maxCount - params.selectedImages.size)
      .isWeChatStyle(true)
      .isCompress(true)
      .compressQuality(10)
      .compressSavePath(picCompressPath)
      .forResult(object : OnResultCallbackListener<LocalMedia> {
        override fun onResult(result: MutableList<LocalMedia>?) {
          if (result.isNullOrEmpty()) {
            resolver.fail(CODE_ACTION_CANCEL, "select failed", "选择失败")
            return
          }
          // 回传h5选择的图片的数据
          val selectedList = ArrayList<SelectedImageBean>(params.maxCount - params.selectedImages.size)
          result.forEach {
            val uuid = UUID.randomUUID().toString()
            val inputStream = FileInputStream(it.compressPath)
            selectedList.add(
              SelectedImageBean(
                uuid,
                Base64.encodeToString(inputStream.readBytes(), Base64.DEFAULT)
              )
            )
            inputStream.close()
            localSelectedImages.add(LocalSelectedImageBean(uuid, it))
          }
          // 记录需要上传的图片数量
          uploadPicCount = selectedList.size
          // 成功回传数据
          resolver.success(JSONArray(gson.toJson(selectedList)))
        }

        override fun onCancel() {
          resolver.fail(CODE_ACTION_CANCEL, "select cancel", "选择取消")
        }

      })
//    ToastUtils.showShort("请到设置中授予存储空间权限")
//    resolver.fail(CODE_PERMISSION_GRANT_FAIL, "check permission READ_EXTERNAL_STORAGE failed", "获取读取外部存储权限失败")
  }

  /**
   * 拍摄图片
   * @param resolver 结果回传
   */
  private fun takePic(resolver: ResultResolver) {
    PictureSelector.create(curTopActivity)
      .openCamera(PictureMimeType.ofImage())
      .imageEngine(GlideEngine.createGlideEngine())
      .isCompress(true)
      .compressQuality(10)
      .compressSavePath(picCompressPath)
      .forResult(object : OnResultCallbackListener<LocalMedia> {
        override fun onResult(result: MutableList<LocalMedia>?) {
          if (result.isNullOrEmpty()) {
            resolver.fail(CODE_ACTION_CANCEL, "take failed", "拍照失败")
            return
          }
          val uuid = UUID.randomUUID().toString()
          localSelectedImages.add(LocalSelectedImageBean(uuid, result[0]))
          val inputStream = FileInputStream(result[0].compressPath)
          // 记录需要上传的图片数量
          uploadPicCount = 1
          // 成功回传数据
          resolver.success(
            JSONArray(
              gson.toJson(
                arrayListOf(
                  SelectedImageBean(
                    uuid,
                    Base64.encodeToString(inputStream.readBytes(), Base64.DEFAULT)
                  )
                )
              )
            )
          )
          inputStream.close()
        }

        override fun onCancel() {
          resolver.fail(CODE_ACTION_CANCEL, "take cancel", "拍照取消")
        }
      })
//    ToastUtils.showShort("请到设置中授予相机权限")
//    resolver.fail(CODE_PERMISSION_GRANT_FAIL, "check permission CAMERA failed", "获取摄像头权限失败")
  }

  /**
   * 上传图片
   * @param id 需要上传的图片id
   * @param resolver 结果回传
   */
  private fun uploadPic(resolver: ResultResolver, id: String) {
    var uploadPic: LocalSelectedImageBean? = null
    localSelectedImages.forEach {
      // 遍历寻找指定图片
      if (id == it.uuid) {
        uploadPic = it
        return@forEach
      }
    }
    uploadPic?.let {
      val file = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // 适配Android10
        // 将图片拷贝到沙盒
        fileCopy(it.data.path, picOriginalPath + it.data.fileName)
        File(picOriginalPath + it.data.fileName)
      } else {
        File(it.data.realPath)
      }
      if (!file.exists()) {
        resolver.fail(CODE_FILE_NOT_EXIST, "file not exist", "文件不存在")
        return
      }
      // 需要上传的图片入队列
      uploadPicQueue.offer(LocalUploadImageBean(it, file, resolver))
      if (uploadPicQueue.size == uploadPicCount) {
        // 接收到所有图片的上传请求后 开始图片上传
        Timber.tag(TAG).d("receive $uploadPicCount upload request, start upload!")
        startUpload()
      }
    } ?: resolver.fail(CODE_FILE_NOT_EXIST, "file not exist", "文件不存在")
  }

  /**
   * 开始执行图片上传
   */
  private fun startUpload() {
    val uploadPic = uploadPicQueue.poll() ?: return
    Timber.tag(TAG).d("${uploadPic.info.uuid} uploading")
    XInsight.uploadImage(
      uploadPic.file, ScenarioType.COMMODITY.scenario,
      object : ImageUploadListener {
        override fun onTaskStart(tag: String?) {

        }

        override fun onTaskProgress(progress: Double?, tag: String?) {

        }

        override fun onTaskFinish(tag: String?) {

        }

        override fun onTaskCancel(tag: String?) {

        }

        override fun onTaskSuccess(bucket: String?, objectKey: String?, imageUploadBean: ImageUploadBean?, tag: String?) {
          Timber.tag(TAG).d("${uploadPic.info.uuid} upload success")
          uploadPic.resolver.success(JSONObject(gson.toJson(imageUploadBean?.image_id?.let { imageId ->
            UploadImageBean(
              uploadPic.info.uuid,
              imageId
            )
          })))
          // 删除临时文件
          clearCache(picOriginalPath + uploadPic.info.data.fileName, uploadPic.info.data.compressPath)
          // 开始下一张图片上传
          startUpload()
        }

        override fun onTaskFailure(code: Int, msg: String?, tag: String?) {
          Timber.tag(TAG).d("${uploadPic.info.uuid} upload failure")
          uploadPic.resolver.fail(CODE_FILE_UPLOAD_FAIL, "upload failed", "上传失败")
          // 删除临时文件
          clearCache(picOriginalPath + uploadPic.info.data.fileName, uploadPic.info.data.compressPath)
          // 开始下一张图片上传
          startUpload()
        }
      }, uploadPic.info.uuid
    )
  }

  /**
   * 文件拷贝
   * 适配Android10
   * @param fromUriStr 源文件uri
   * @param targetPath 目标文件全路径
   */
  private fun fileCopy(fromUriStr: String?, targetPath: String?) {
    val inputStream = YdBaseApplication.context.contentResolver.openInputStream(Uri.parse(fromUriStr))
    val outputStream = FileOutputStream(targetPath)
    inputStream?.copyTo(outputStream)
    inputStream?.close()
    outputStream.close()
  }

  /**
   * 清理缓存
   * @param originalPath 拷贝到沙盒的原图路径
   * @param compressPath 压缩后的路径
   */
  private fun clearCache(originalPath: String, compressPath: String) {
    File(originalPath).apply { if (exists()) delete() }
    File(compressPath).apply { if (exists()) delete() }
  }

  /**
   * 业务场景类型
   * 图片上传时需要区分
   */
  enum class ScenarioType(val scenario: String) {
    // 商品
    COMMODITY("shq_goods")
  }

}
