package com.dlc.eboard.ui.sign

import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModelProvider
import com.dlc.eboard.LabApp
import com.dlc.eboard.R
import com.dlc.eboard.common.CommonUtils
import com.dlc.eboard.common.SignType
import com.dlc.eboard.databinding.FragmentSafetyCheckBinding
import com.dlc.eboard.event.OnlineUserEvent
import com.dlc.eboard.iflytek.AudioPlayerManager
import com.dlc.eboard.sp.DeviceSp
import com.dlc.eboard.ui.auth.fragment.CardAuthFragment
import com.dlc.eboard.ui.dialog.ResultEnum
import com.dlc.eboard.ui.dialog.SafetyCheckResultDialog
import com.rc.core.ui.fragment.RcBaseFragment
import com.rc.core.util.MediaUtils
import com.rc.core.util.ifNullOrEmpty
import com.rc.httpcore.client.ApiRepository
import com.rc.httpcore.exception.AICheckException
import com.rc.httpcore.exception.NetException
import com.rc.httpcore.vo.request.CheckInAllReq
import com.rc.httpcore.vo.request.PatrolSignInReq
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 SafetyCheckFragment :
    RcBaseFragment<FragmentSafetyCheckBinding>() {

    companion object {
        // 5s后，开始拍照/录像
        private const val MAX_TIME = 5

        private const val WHAT_REPEAT = 1
        private const val WHAT_TIME_OUT = 2
        private const val WHAT_RECORDING = 3
        private const val WHAT_COUNT_DOWN = 4

        private const val REPEAT_TIME = 10 * 1000L
        private const val TIME_OUT = 30 * 1000L
        private const val RECORDING_HEART = 300L

        fun newInstance(id: String): SafetyCheckFragment {
            val args = Bundle()
            args.putString("id", id)
            val fragment = SafetyCheckFragment()
            fragment.arguments = args
            return fragment
        }

    }

    private var mCounter = 0

    private var mCurrentCheckItem: CheckItem? = null
    private var mSignTyp = SignType.ACCESS.code

    private var mJumpSafetyCheck = false

    private var isMatching = false

    private var mSignId: String? = null

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

    private var mFragmentCallback: SignInFragmentCallback? = null
    private var mCountDown: ICountDown? = null

    private lateinit var mFotoapparat : Fotoapparat

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

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

    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 {
                handlerTakePictureResult(photoFile)
            }
    }

    override fun onResume() {
        super.onResume()
        mFotoapparat.start()
    }

    override fun onPause() {
        super.onPause()
        mFotoapparat.stop()
    }

    override fun createViewBinding(
        inflater: LayoutInflater,
        container: ViewGroup?
    ) = FragmentSafetyCheckBinding.inflate(inflater, container, false)

    override fun initViews(savedInstanceState: Bundle?) {
        mSignId = arguments?.getString("id")
        viewModel.checkItemData.observe(viewLifecycleOwner) { data ->
            mCurrentCheckItem = data
            data?.let {
                preStartCheck()
            }
        }
        viewModel.signTypeData.observe(viewLifecycleOwner) { data ->
            mSignTyp = data ?: SignType.ACCESS.code
        }
        viewModel.signFaceData.observe(viewLifecycleOwner) { data ->
            data?.let {
                mSignId = data.id
                mCurrentCheckItem = CheckItem(data.code)
                preStartCheck()
            }
        }
        mFotoapparat = Fotoapparat.with(requireContext())
            .into(viewBinding.cameraView)
            .lensPosition(back())
            .frameProcessor(
                FaceDetectorProcessor.with(requireContext())
                    .listener { faces ->
                        viewBinding.rectanglesView.setRectangles(faces)
                    }.build()
            )
            .cameraErrorCallback { onError(it.message) }
            .logger(loggers(logcat(), fileLogger(requireContext())))
            .build()

        // 开始检测
        viewBinding.startCheck.setOnClickListener {
            // 移除语音播报提示检测Message
            mCountDown?.onStopCountDown()
            mVoiceHandler.removeMessages(WHAT_REPEAT)
            mVoiceHandler.removeMessages(WHAT_TIME_OUT)

            // 按钮不可用
            viewBinding.startCheck.isEnabled = false
            // 倒计时5s，结束后，拍照开始
            mCounter = 0
            viewBinding.countDown.visibility = View.VISIBLE
            mCountDownHandler.sendEmptyMessage(WHAT_COUNT_DOWN)
        }

        // 电子牌 + 巡查签到
        if ("1" == LabApp.sLabConfig?.isStart
            && SignType.PATROL.code == mSignTyp) {
            preStartCheck()
        }
    }

    private fun preStartCheck() {
        mCountDown?.onStartCountDown()
        dispatchCheckTip()
        mVoiceHandler.sendEmptyMessageDelayed(WHAT_TIME_OUT, TIME_OUT)
    }

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

        override fun handleMessage(msg: Message) {
            if (WHAT_COUNT_DOWN == msg.what) {
                val countDown = MAX_TIME - mCounter
                if (countDown <= 0) {
                    viewBinding.countDown.visibility = View.GONE
                    // 倒计时结束，开始拍照
                    takePicture()
                } else {
                    viewBinding.countDown.text = "$countDown"
                    sendEmptyMessageDelayed(WHAT_COUNT_DOWN, 1000)
                }
                mCounter++
            }
        }
    }

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

        override fun handleMessage(msg: Message) {
            val visibility = msg.obj as? Boolean ?: false
            viewBinding.recording.visibility =
                if (visibility) View.VISIBLE else View.INVISIBLE

            val message = obtainMessage(WHAT_RECORDING, !visibility)
            sendMessageDelayed(message, RECORDING_HEART)
        }
    }

    private fun dispatchCheckTip() {
        val message = "穿戴安全准入检测，请确认处于检测区域后，点击开始检测。"
        AudioPlayerManager.speakVoice(R.raw.audio_check_ready)
        mVoiceHandler.sendEmptyMessageDelayed(WHAT_REPEAT, REPEAT_TIME)
    }

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

        override fun handleMessage(msg: Message) {
            when (msg.what) {
                WHAT_REPEAT -> dispatchCheckTip()
                WHAT_TIME_OUT -> dispatchTimeOut()
            }
        }
    }

    private fun dispatchTimeOut() {
        mCountDown?.onStopCountDown()
        AudioPlayerManager.speakVoice(R.raw.audio_failed_sign)
        SafetyCheckResultDialog(requireContext(), ResultEnum.FAIL, "签到失败", 3000).apply {
            setCancelable(false)
            setOnDismissListener { requireActivity().finish() }
            show()
        }
    }

    private fun handlerTakePictureResult(photoFile: File) {
        viewBinding.startCheck.isEnabled = true

        // 1) 显示loading
        val message = "检测中，请勿操作屏幕。"
        AudioPlayerManager.speakVoice(R.raw.audio_checking)
        mCheckLoading =
            SafetyCheckResultDialog(requireContext(), ResultEnum.LOADING, message).apply {
                setCancelable(false)
                show()
            }
        // 2) 调用后台接口：穿戴安全准入检测
        callCheckApi(photoFile)
    }

    /*
     * 调用检测接口
     */
    private fun callCheckApi(photoFile: File) {
        if (null == LabApp.sLabConfig) {
            showToast("暂无实验室信息，即将重新获取")
            CommonUtils.restartApp(requireActivity())
            return
        }
        val pram = CheckInAllReq().apply {
            subId = LabApp.sLabConfig!!.labId
            id = mSignId
            file = photoFile
        }
        val disposable = ApiRepository.checkInAll(SignType.PATROL.code == mSignTyp, pram)
            .subscribe({
                // 删除源文件
                MediaUtils.deleteFile(photoFile)
                // 检测通过：跳转到下一级检测
                mCheckLoading?.dismiss()
                dispatchCheckSuccess()
            }, { throwable ->
                // 删除源文件
                MediaUtils.deleteFile(photoFile)
                // 检测失败：弹出提示，重新检测
                mCheckLoading?.dismiss()
                if (!pretreatmentError(throwable, "穿戴检测")) {
                    throwable.printStackTrace()
                    showNetError(throwable)

                    // 出现其它错误时，重新开始计时。
                    mCountDown?.onStartCountDown()
                    dispatchCheckTip()
                    mVoiceHandler.sendEmptyMessageDelayed(WHAT_TIME_OUT, TIME_OUT)
                }
            })
        addDisposable(disposable)
    }

    private fun dispatchCheckSuccess() {
        val message = "穿戴安全准入检测完成。"
        val dialog = SafetyCheckResultDialog(
            requireContext(),
            ResultEnum.SUCCESS,
            message
        ).apply {
            setCancelable(false)
            show()
        }
        AudioPlayerManager.speakVoice(R.raw.audio_wear_check_completed) {
            dialog.dismiss()
            callSignInSubmitApi()
        }
    }

    private fun callSignInSubmitApi() {
        showLoading("签到中...")
        val disposable = if (SignType.PATROL.code == mSignTyp) {
            val param = PatrolSignInReq().apply {
                num = CommonUtils.getAndroidId()
                userId = LabApp.sUserVo?.userId
            }
            ApiRepository.signInWithPatrol(param)
        } else {
            val code = mCurrentCheckItem?.code ?: ""
            ApiRepository.signIn(mJumpSafetyCheck, code, mSignId!!)
        }
            .subscribe({ success ->
                dismissLoading()
                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)) {
                    throwable.printStackTrace()
                    showNetError(throwable)
                }
            })
        addDisposable(disposable)
    }

    private fun pretreatmentSignInError(throwable: Throwable): Boolean {
        if (throwable is NetException) {
            // 600- 签到&签出已超时，请重新刷卡重试！
            if ("600" == throwable.code) {
                dispatchError_600(throwable.message)
                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 dispatchError_600(message: String?) {
        AlertDialog.Builder(requireContext())
            .setTitle("提示")
            .setMessage(message)
            .setPositiveButton("确定") { _, _ ->
                val fragment =
                    requireFragmentManager().findFragmentByTag(CardAuthFragment::class.java.simpleName)
                            as? CardAuthFragment ?: CardAuthFragment.newInstance()
                mFragmentCallback?.switchFragment(fragment)
            }.show()
    }

    private fun pretreatmentError(throwable: Throwable, text: String): Boolean {
        if (throwable is AICheckException) {
            // 600- 签到&签出已超时，请重新刷卡重试！
            if ("600" == throwable.code) {
                dispatchError_600(throwable.message)
                return true
            } else if ("700" == throwable.code) {
                // 700- 检测失败，但是可以跳过此检测项
                mJumpSafetyCheck = true
                dispatchCheckSuccess()
                return true
            } else {
                // 后台返回了其它code
                val front = if ("300" == throwable.code) decodeCheckCode(throwable.data) else null
                val message = "${front ?: ""}穿戴检测未通过，请再次点击开始检测。"
                AudioPlayerManager.speakVoice(speakCheckFailed(throwable.data))
                SafetyCheckResultDialog(requireContext(), ResultEnum.WARNING, message).show()
                return true
            }
        } else if (throwable is NetException) {
            val message = throwable.message.ifNullOrEmpty("${text}失败，请联系实验室管理员。")
            //AudioPlayerManager.speakVoice(R.raw.audio_failed_sign_admin)
            SafetyCheckResultDialog(requireContext(), ResultEnum.FAIL, message, 3000).apply {
                setCancelable(false)
                show()
            }
            return true
        }
        return false
    }

    private fun speakCheckFailed(code: String?): Int {
        return when (code) {
            "1" -> R.raw.audio_wear_check_hmj_failed
            "2" -> R.raw.audio_wear_check_syf_failed
            "3" -> R.raw.audio_wear_check_st_failed
            else -> R.raw.audio_wear_check_failed
        }
    }

    private fun decodeCheckCode(code: String?): String? {
        val laboratoryVo = DeviceSp.loadLaboratoryVo(requireContext())
        if (null == laboratoryVo || laboratoryVo.inCheck.isNullOrEmpty()) return null
        return laboratoryVo.inCheck.find { it.code == code }?.name
    }

    private var mCheckLoading: SafetyCheckResultDialog? = null

    private fun onError(message: String?) {
        dismissLoading()
        message?.let { showToast(it) }
        viewBinding.startCheck.isEnabled = true

        // 拍照错误时，重新开始计时。
        mCountDown?.onStartCountDown()
        dispatchCheckTip()
        mVoiceHandler.sendEmptyMessageDelayed(WHAT_TIME_OUT, TIME_OUT)
    }

    override fun onDestroy() {
        mCountDown?.onStopCountDown()
        mVoiceHandler.removeMessages(WHAT_REPEAT)
        mVoiceHandler.removeMessages(WHAT_TIME_OUT)
        mRecordingHandler.removeMessages(WHAT_RECORDING)
        mCountDownHandler.removeMessages(WHAT_COUNT_DOWN)
        super.onDestroy()
    }

}