package com.dlc.laboratory.ui.sign

import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.*
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.bumptech.glide.Glide
import com.dlc.eboard.LabApp
import com.dlc.eboard.R
import com.dlc.eboard.common.CommonUtils
import com.dlc.eboard.common.Constants
import com.dlc.eboard.common.SignType
import com.dlc.eboard.databinding.FragmentSignInFaceMatchingBinding
import com.dlc.eboard.event.OnlineUserEvent
import com.dlc.eboard.iflytek.AudioPlayerManager
import com.dlc.eboard.mqtt.AudioService
import com.dlc.eboard.sp.DeviceSp
import com.dlc.eboard.ui.dialog.CheckResultDialog
import com.dlc.eboard.ui.dialog.ResultEnum
import com.dlc.eboard.ui.dialog.SafetyCheckResultDialog
import com.dlc.eboard.ui.sign.CheckItem
import com.dlc.eboard.ui.sign.SafetyCheckFragment
import com.dlc.eboard.ui.sign.SignInFragmentCallback
import com.dlc.eboard.ui.sign.SignViewModel
import com.rc.core.ui.fragment.RcBaseFragment
import com.rc.core.util.Format
import com.rc.core.util.MediaUtils
import com.rc.core.util.ifNullOrEmpty
import com.rc.httpcore.client.ApiRepository
import com.rc.httpcore.exception.NetException
import com.rc.httpcore.vo.response.SignInCheckResp
import io.fotoapparat.Fotoapparat
import io.fotoapparat.facedetector.processor.FaceDetectorProcessor
import io.fotoapparat.log.fileLogger
import io.fotoapparat.log.logcat
import io.fotoapparat.log.loggers
import io.fotoapparat.selector.back
import io.fotoapparat.selector.front
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.text.SimpleDateFormat
import java.util.*

/**
 * 签到人脸比对
 *
 * @author ReiChin_
 */
class SignInFaceMatchingFragment :
    RcBaseFragment<FragmentSignInFaceMatchingBinding>() {

    companion object {
        private const val WHAT_TAKE_PICTURE = 1
        private const val TIMEOUT_TAKE_PICTURE = 2000L
    }

    private var mFragmentCallback: SignInFragmentCallback? = null

    override fun onAttach(context: Context) {
        super.onAttach(context)
        mFragmentCallback = context as? SignInFragmentCallback
    }

    override fun onDetach() {
        super.onDetach()
        mFragmentCallback = null
    }

    private var mStudentInfo: SignInCheckResp? = null

    private var isMatching = true

    private lateinit var mFotoapparat: Fotoapparat

    private var mSignTyp = SignType.ACCESS.code

    override fun createViewBinding(
        inflater: LayoutInflater,
        container: ViewGroup?
    ): FragmentSignInFaceMatchingBinding {
        return FragmentSignInFaceMatchingBinding.inflate(inflater, container, false)
    }

    override fun initViews(savedInstanceState: Bundle?) {
        viewModel.signTypeData.observe(viewLifecycleOwner) { data ->
            mSignTyp = data ?: SignType.ACCESS.code
        }

        isMatching = false
        mFotoapparat = Fotoapparat.with(requireContext())
            .into(viewBinding.cameraView)
            .lensPosition(back())
            .frameProcessor(
                FaceDetectorProcessor.with(requireContext())
                    .listener { faces ->
                        viewBinding.rectanglesView.setRectangles(faces)
//                        if (!isMatching && isResumed && !faces.isNullOrEmpty()) takePicture()
                        if (!isMatching && isResumed) {
                            isMatching = true
                            mTakePictureHandler.sendEmptyMessageDelayed(WHAT_TAKE_PICTURE, TIMEOUT_TAKE_PICTURE)
                        }
                    }.build()
            )
            .logger(loggers(logcat(), fileLogger(requireContext())))
            .build()

        callStudentCheckApi()
    }

    private val mTakePictureHandler = object : Handler(Looper.getMainLooper()) {

        override fun handleMessage(msg: Message) {
            if (!isResumed) return
            if (WHAT_TAKE_PICTURE == msg.what) takePicture()
        }
    }

    private fun takePicture() {
//        isMatching = true
        val format = SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.getDefault())
        val fileName = "${format.format(Calendar.getInstance().time)}.jpg"
        val photoFile = File(requireContext().getExternalFilesDir("photos"), fileName)
        mFotoapparat.takePicture()
            .saveToFile(photoFile)
            .whenAvailable {
                callFaceMatchingApi(photoFile)
            }
    }

    override fun onResume() {
        super.onResume()
        mFotoapparat.start()
        isMatching = false
    }

    override fun onPause() {
        super.onPause()
        mFotoapparat.stop()
        mTakePictureHandler.removeCallbacksAndMessages(null)
    }

    private fun callStudentCheckApi() {
        if (null == LabApp.sLabConfig) {
            showToast("暂无实验室信息，即将重新获取")
            CommonUtils.restartApp(requireActivity())
            return
        }
        val username = requireArguments().getString("username")!!

        showLoading("查询中...")
        val disposable = ApiRepository.signInCheck(false, LabApp.sLabConfig!!.labId, username)
            .subscribe({ data ->
                dismissLoading()
                mStudentInfo = data
                refreshUI()
            }, { throwable ->
                dismissLoading()
                throwable.printStackTrace()
                showNetError(throwable)
            })
        addDisposable(disposable)
    }

    @SuppressLint("SetTextI18n")
    private fun refreshUI() {
        mStudentInfo?.let { data ->
            // 头像
            Glide.with(this)
                .load(data.singInUser.avatar)
                .placeholder(R.mipmap.icon_sign_in_avatar)
                .error(R.mipmap.icon_sign_in_avatar)
                .into(viewBinding.personProfile)
            // 姓名
            viewBinding.personName.text = data.singInUser.nickName
            // 学号
            viewBinding.schoolNumber.text = "学号：${data.singInUser.userName ?: ""}"
            // 准入资格到期时间
            viewBinding.expirationDate.text =
                "准入资格到期时间：${
                    Format.formatZonedDateTime(
                        data.singInUser.expirationDate,
                        "yyyy年MM月dd日"
                    )
                }"
            // 违规行为
            viewBinding.violationContent.text =
                "违规行为：${data.singInViolation.violationContent ?: ""}"
            // 违规地点
            viewBinding.subjectName.text = "违规地点：${data.singInViolation.subjectName ?: ""}"
            // 违规时间
            viewBinding.createTime.text = "违规时间：${
                Format.formatZonedDateTime(
                    data.singInViolation.createTime,
                    "yyyy年MM月dd日 HH:mm"
                )
            }"
            // 提示信息
            if (!data.message.isNullOrEmpty()) {
                viewBinding.infoLayout.visibility = View.VISIBLE
                val message = data.message.reduce { acc, s -> "$acc\n$s" }
//                    ?.let { it.substring(0, it.length - 1) }
                viewBinding.signInTips.text = message
                textParseVideo(data.message[0])
            } else {
                viewBinding.infoLayout.visibility = View.INVISIBLE
            }

//            viewBinding.camera.isEnabled = data.state
            if (!data.state) {
                // 弹出核验结果的Dialog，，5秒后关闭Dialog
                val dialog = CheckResultDialog()
                dialog.setSuccess(data.state)
                dialog.show(requireFragmentManager(), CheckResultDialog::class.java.simpleName)
            } else {
                // 核验通过，直接人脸识别
                isMatching = false
            }
        }
    }

    private fun textParseVideo(text: String?) {
        if (text.isNullOrEmpty()) return

        showLoading()
        val disposable = ApiRepository.textParseVideo(text)
            .subscribe({ data ->
                dismissLoading()
                mAudioBinder?.startPlayer(data.url)
            }, { throwable ->
                dismissLoading()
                throwable.printStackTrace()
                showNetError(throwable)
            })
        addDisposable(disposable)
    }

    private var mAudioBinder: AudioService.AudioBinder? = null

    private val mAudioConnection: ServiceConnection = object : ServiceConnection {

        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            mAudioBinder = service as AudioService.AudioBinder
        }

        override fun onServiceDisconnected(name: ComponentName) {
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requireContext().bindService(
            Intent(requireContext(), AudioService::class.java), mAudioConnection,
            AppCompatActivity.BIND_AUTO_CREATE
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        requireContext().unbindService(mAudioConnection)
    }

    private fun callFaceMatchingApi(photoFile: File) {
        val code = mStudentInfo?.code ?: return

        showLoading("比对中...")
        val disposable = ApiRepository.signFaceMatching(photoFile, code)
            .subscribe({ id ->
                dismissLoading()
                MediaUtils.deleteFile(photoFile)
                dispatchSafetyCheck(id)
            }, { throwable ->
                dismissLoading()
                MediaUtils.deleteFile(photoFile)
                if (!pretreatmentError(throwable)) {
                    isMatching = false
                    throwable.printStackTrace()
                    showNetError(throwable)
                }
            })
        addDisposable(disposable)
    }

    private fun dispatchSafetyCheck(id: String) {
        val laboratoryVo = DeviceSp.loadLaboratoryVo(requireContext())
        if (null == laboratoryVo) {
            showToast("暂无实验室信息，即将重新获取")
            CommonUtils.restartApp(requireActivity())
            return
        }
        val checkItem = CheckItem(mStudentInfo!!.code)
        if (laboratoryVo.inCheck.isNullOrEmpty()) {
            // 调用最后的提交方法
            callSignInSubmitApi(false, "", id)
        } else if (inSignWhiteList()) {
            // 签到/签退白名单: 跳过AI检测
            callSignInSubmitApi(true, checkItem.code, id)
        } else {
            viewModel.setCheckItem(checkItem)
            val fragment =
                requireFragmentManager().findFragmentByTag(SafetyCheckFragment::class.java.simpleName)
                        as? SafetyCheckFragment ?: SafetyCheckFragment.newInstance(id)
            fragment.arguments = Bundle().apply {
                putString("id", id)
            }
            mFragmentCallback?.switchFragment(fragment)
        }
    }

    private fun inSignWhiteList(): Boolean {
        return Constants.Mock.SIGN_WHITE_LIST.contains(mStudentInfo?.singInUser?.userId)
    }

    private val viewModel by lazy {
        ViewModelProvider(requireActivity(), ViewModelProvider.NewInstanceFactory()).get(
            SignViewModel::class.java
        )
    }

    private fun callSignInSubmitApi(jumpSafetyCheck: Boolean, code: String, id: String) {
        showLoading("保存中...")
        // 是否为巡查签到
        val disposable = ApiRepository.signIn(jumpSafetyCheck, code, id)
            .subscribe({ success ->
                if (success) {
                    EventBus.getDefault().post(OnlineUserEvent())
                    val message = "签到成功"
                    AudioPlayerManager.speakVoice(R.raw.audio_success_sign)
                    SafetyCheckResultDialog(
                        requireContext(),
                        ResultEnum.SUCCESS,
                        message,
                        3000
                    ).apply {
                        setCancelable(false)
                        setOnDismissListener { requireActivity().finish() }
                        show()
                    }
                }
            }, { throwable ->
                dismissLoading()
                if (!pretreatmentSignInError(throwable)) {
                    isMatching = false
                    throwable.printStackTrace()
                    showNetError(throwable)
                }
            })
        addDisposable(disposable)
    }

    private fun pretreatmentSignInError(throwable: Throwable): Boolean {
        if (throwable is NetException) {
            // 600- 签到&签出已超时，请重新刷卡重试！
            if ("600" == throwable.code) {
                AlertDialog.Builder(requireContext())
                    .setTitle("提示")
                    .setMessage(throwable.message)
                    .setPositiveButton("确定") { _, _ ->
                        callStudentCheckApi()
                    }.show()
                return true
            } else {
                // 后台返回的其它errorCode
                val message = throwable.message.ifNullOrEmpty("签到失败，请联系实验室管理员。")
                AudioPlayerManager.speakVoice(R.raw.audio_failed_sign_admin)
                SafetyCheckResultDialog(requireContext(), ResultEnum.FAIL, message, 3000).apply {
                    setCancelable(false)
                    setOnDismissListener { requireActivity().finish() }
                    show()
                }
                return true
            }
        }
        return false
    }

    private fun pretreatmentError(throwable: Throwable): Boolean {
        if (throwable is NetException) {
            // 600- 签到&签出已超时，请重新刷卡重试！
            if ("600" == throwable.code) {
                AlertDialog.Builder(requireContext())
                    .setTitle("提示")
                    .setMessage(throwable.message)
                    .setPositiveButton("确定") { _, _ ->
                        callStudentCheckApi()
                    }.show()
                return true
            } else if ("500" == throwable.code &&
                true == throwable.message?.startsWith("人脸对比不符")
            ) {
                // 500- 人脸对比不符！
                AlertDialog.Builder(requireContext())
                    .setCancelable(false)
                    .setTitle("提示")
                    .setMessage(throwable.message)
                    .setPositiveButton("确定") { _, _ ->
                        isMatching = false
                    }.show()
                return true
            } else if ("500" == throwable.code
                && throwable.message == "识别中") {
                isMatching = false
                return true
            }
        }
        return false
    }

}