package com.idormy.sms.forwarder.fragment

import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RadioGroup
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.WidgetItemAdapter
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.databinding.FragmentClientBinding
import com.idormy.sms.forwarder.server.model.BaseResponse
import com.idormy.sms.forwarder.server.model.ConfigData
import com.idormy.sms.forwarder.utils.*
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xhttp2.XHttp
import com.xuexiang.xhttp2.cache.model.CacheMode
import com.xuexiang.xhttp2.callback.SimpleCallBack
import com.xuexiang.xhttp2.exception.ApiException
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xpage.base.XPageFragment
import com.xuexiang.xpage.core.PageOption
import com.xuexiang.xpage.model.PageInfo
import com.xuexiang.xrouter.utils.TextUtils
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
import com.xuexiang.xui.utils.CountDownButtonHelper
import com.xuexiang.xui.utils.DensityUtils
import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.utils.WidgetUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.data.ConvertTools

@Suppress("PrivatePropertyName", "PropertyName")
@Page(name = "主动控制·客户端")
class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> {

    val TAG: String = ClientFragment::class.java.simpleName
    private var appContext: App? = null
    private var serverConfig: ConfigData? = null
    private var serverHistory: MutableMap<String, String> = mutableMapOf()
    private var mCountDownHelper: CountDownButtonHelper? = null

    override fun initViews() {
        appContext = requireActivity().application as App

        //测试按钮增加倒计时，避免重复点击
        mCountDownHelper = CountDownButtonHelper(binding!!.btnServerTest, 3)
        mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
            override fun onCountDown(time: Int) {
                binding!!.btnServerTest.text = String.format(getString(R.string.seconds_n), time)
            }

            override fun onFinished() {
                binding!!.btnServerTest.text = getString(R.string.server_test)
            }
        })

        WidgetUtils.initGridRecyclerView(binding!!.recyclerView, 3, DensityUtils.dp2px(1f))
        val widgetItemAdapter = WidgetItemAdapter(CLIENT_FRAGMENT_LIST)
        widgetItemAdapter.setOnItemClickListener(this)
        binding!!.recyclerView.adapter = widgetItemAdapter

        //取出历史记录
        val history = HttpServerUtils.serverHistory
        if (!TextUtils.isEmpty(history)) {
            serverHistory = Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
        }

        if (CommonUtils.checkUrl(HttpServerUtils.serverAddress)) queryConfig(false)
    }

    override fun initTitle(): TitleBar? {
        val titleBar = super.initTitle()!!.setImmersive(false)
        //纯客户端模式
        if (SettingUtils.enablePureClientMode) {
            titleBar.setTitle(R.string.app_name).setSubTitle(getString(R.string.menu_client)).disableLeftView()
            titleBar.addAction(object : TitleBar.ImageAction(R.drawable.ic_logout) {
                @SingleClick
                override fun performAction(view: View) {
                    XToastUtils.success(getString(R.string.exit_pure_client_mode))
                    SettingUtils.enablePureClientMode = false
                    try {
                        Thread.sleep(500) //延迟500毫秒，避免退出时enablePureClientMode还没保存
                        XUtil.exitApp()
                    } catch (e: InterruptedException) {
                        e.printStackTrace()
                    }
                }
            })
        } else {
            titleBar.setTitle(R.string.menu_client)
        }
        return titleBar
    }

    override fun viewBindingInflate(
        inflater: LayoutInflater,
        container: ViewGroup,
    ): FragmentClientBinding {
        return FragmentClientBinding.inflate(inflater, container, false)
    }

    override fun initListeners() {
        binding!!.etServerAddress.setText(HttpServerUtils.serverAddress)
        binding!!.etServerAddress.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
            override fun afterTextChanged(s: Editable) {
                HttpServerUtils.serverAddress = binding!!.etServerAddress.text.toString().trim().trimEnd('/')
            }
        })

        //安全措施
        var safetyMeasuresId = R.id.rb_safety_measures_none
        when (HttpServerUtils.clientSafetyMeasures) {
            1 -> {
                safetyMeasuresId = R.id.rb_safety_measures_sign
                binding!!.tvSignKey.text = getString(R.string.sign_key)
            }
            2 -> {
                safetyMeasuresId = R.id.rb_safety_measures_rsa
                binding!!.tvSignKey.text = getString(R.string.public_key)
            }
            3 -> {
                safetyMeasuresId = R.id.rb_safety_measures_sm4
                binding!!.tvSignKey.text = getString(R.string.sm4_key)
            }
            else -> {
                binding!!.layoutSignKey.visibility = View.GONE
            }
        }
        binding!!.rgSafetyMeasures.check(safetyMeasuresId)
        binding!!.rgSafetyMeasures.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
            var safetyMeasures = 0
            binding!!.layoutSignKey.visibility = View.VISIBLE
            when (checkedId) {
                R.id.rb_safety_measures_sign -> {
                    safetyMeasures = 1
                    binding!!.tvSignKey.text = getString(R.string.sign_key)
                }
                R.id.rb_safety_measures_rsa -> {
                    safetyMeasures = 2
                    binding!!.tvSignKey.text = getString(R.string.public_key)
                }
                R.id.rb_safety_measures_sm4 -> {
                    safetyMeasures = 3
                    binding!!.tvSignKey.text = getString(R.string.sm4_key)
                }
                else -> {
                    binding!!.layoutSignKey.visibility = View.GONE
                }
            }
            HttpServerUtils.clientSafetyMeasures = safetyMeasures
        }

        binding!!.etSignKey.setText(HttpServerUtils.clientSignKey)
        binding!!.etSignKey.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
            override fun afterTextChanged(s: Editable) {
                HttpServerUtils.clientSignKey = binding!!.etSignKey.text.toString().trim()
            }
        })

        binding!!.btnWechatMiniprogram.setOnClickListener(this)
        binding!!.btnServerHistory.setOnClickListener(this)
        binding!!.btnServerTest.setOnClickListener(this)
    }

    @SingleClick
    override fun onClick(v: View) {
        when (v.id) {
            R.id.btn_wechat_miniprogram -> {
                if (HttpServerUtils.safetyMeasures != 3) {
                    XToastUtils.error("微信小程序只支持SM4加密传输！请前往主动控制·服务端修改安全措施！")
                    return
                }
                CommonUtils.previewPicture(this, getString(R.string.url_wechat_miniprogram), null)
            }
            R.id.btn_server_history -> {
                if (serverHistory.isEmpty()) {
                    XToastUtils.warning(getString(R.string.no_server_history))
                    return
                }
                Log.d(TAG, "serverHistory = $serverHistory")

                MaterialDialog.Builder(requireContext()).title(R.string.server_history).items(serverHistory.keys).itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
                    //XToastUtils.info("$which: $text")
                    val matches = Regex("【(.*)】(.*)", RegexOption.IGNORE_CASE).findAll(text).toList().flatMap(MatchResult::groupValues)
                    Log.i(TAG, "matches = $matches")
                    if (matches.isNotEmpty()) {
                        binding!!.etServerAddress.setText(matches[2])
                    } else {
                        binding!!.etServerAddress.setText(text)
                    }
                    val signKey = serverHistory[text].toString()
                    if (!TextUtils.isEmpty(signKey)) {
                        val keyMatches = Regex("(.*)##(.*)", RegexOption.IGNORE_CASE).findAll(signKey).toList().flatMap(MatchResult::groupValues)
                        Log.i(TAG, "keyMatches = $keyMatches")
                        if (keyMatches.isNotEmpty()) {
                            binding!!.etSignKey.setText(keyMatches[1])
                            var safetyMeasuresId = R.id.rb_safety_measures_none
                            when (keyMatches[2]) {
                                "1" -> {
                                    safetyMeasuresId = R.id.rb_safety_measures_sign
                                    binding!!.tvSignKey.text = getString(R.string.sign_key)
                                }
                                "2" -> {
                                    safetyMeasuresId = R.id.rb_safety_measures_rsa
                                    binding!!.tvSignKey.text = getString(R.string.public_key)
                                }
                                "3" -> {
                                    safetyMeasuresId = R.id.rb_safety_measures_sm4
                                    binding!!.tvSignKey.text = getString(R.string.sm4_key)
                                }
                                else -> {
                                    binding!!.tvSignKey.visibility = View.GONE
                                    binding!!.etSignKey.visibility = View.GONE
                                }
                            }
                            binding!!.rgSafetyMeasures.check(safetyMeasuresId)
                        } else {
                            binding!!.etSignKey.setText(serverHistory[text])
                        }
                    }
                    true // allow selection
                }.positiveText(R.string.select).negativeText(R.string.cancel).neutralText(R.string.clear_history).neutralColor(ResUtils.getColors(R.color.red)).onNeutral { _: MaterialDialog?, _: DialogAction? ->
                    serverHistory.clear()
                    HttpServerUtils.serverHistory = ""
                }.show()
            }
            R.id.btn_server_test -> {
                if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
                    XToastUtils.error(getString(R.string.invalid_service_address))
                    return
                }
                queryConfig(true)
            }
            else -> {}
        }
    }

    override fun onItemClick(itemView: View, item: PageInfo, position: Int) {
        try {
            if (item.name != ResUtils.getString(R.string.api_clone) && !CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
                XToastUtils.error(getString(R.string.invalid_service_address))
                serverConfig = null
                return
            }
            if (serverConfig == null && item.name != ResUtils.getString(R.string.api_clone)) {
                XToastUtils.error(getString(R.string.click_test_button_first))
                return
            }
            if (serverConfig != null && ((item.name == ResUtils.getString(R.string.api_sms_send) && !serverConfig!!.enableApiSmsSend) || (item.name == ResUtils.getString(R.string.api_sms_query) && !serverConfig!!.enableApiSmsQuery) || (item.name == ResUtils.getString(R.string.api_call_query) && !serverConfig!!.enableApiCallQuery) || (item.name == ResUtils.getString(R.string.api_contact_query) && !serverConfig!!.enableApiContactQuery) || (item.name == ResUtils.getString(R.string.api_battery_query) && !serverConfig!!.enableApiBatteryQuery) || (item.name == ResUtils.getString(R.string.api_wol) && !serverConfig!!.enableApiWol))) {
                XToastUtils.error(getString(R.string.disabled_on_the_server))
                return
            }
            @Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(item.classPath) as Class<XPageFragment>) //跳转的fragment
                .setNewActivity(true).open(this)
        } catch (e: Exception) {
            e.printStackTrace()
            XToastUtils.error(e.message.toString())
        }
    }

    private fun queryConfig(needToast: Boolean) {
        val requestUrl: String = HttpServerUtils.serverAddress + "/config/query"
        Log.i(TAG, "requestUrl:$requestUrl")

        val msgMap: MutableMap<String, Any> = mutableMapOf()
        val timestamp = System.currentTimeMillis()
        msgMap["timestamp"] = timestamp

        val clientSignKey = HttpServerUtils.clientSignKey
        if (HttpServerUtils.clientSafetyMeasures != 0 && TextUtils.isEmpty(clientSignKey)) {
            if (needToast) XToastUtils.error("请输入签名密钥/RSA公钥/SM4密钥")
            return
        }

        if (HttpServerUtils.clientSafetyMeasures == 1 && !TextUtils.isEmpty(clientSignKey)) {
            msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
        }
        val dataMap: MutableMap<String, Any> = mutableMapOf()
        msgMap["data"] = dataMap

        var requestMsg: String = Gson().toJson(msgMap)
        Log.i(TAG, "requestMsg:$requestMsg")

        val postRequest = XHttp.post(requestUrl).keepJson(true).timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
            .cacheMode(CacheMode.NO_CACHE).timeStamp(true)

        when (HttpServerUtils.clientSafetyMeasures) {
            2 -> {
                try {
                    val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
                    requestMsg = Base64.encode(requestMsg.toByteArray())
                    requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
                    Log.i(TAG, "requestMsg: $requestMsg")
                } catch (e: Exception) {
                    if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
                    e.printStackTrace()
                    return
                }
                postRequest.upString(requestMsg)
            }
            3 -> {
                try {
                    val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
                    //requestMsg = Base64.encode(requestMsg.toByteArray())
                    val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
                    requestMsg = ConvertTools.bytes2HexString(encryptCBC)
                    Log.i(TAG, "requestMsg: $requestMsg")
                } catch (e: Exception) {
                    if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
                    e.printStackTrace()
                    return
                }
                postRequest.upString(requestMsg)
            }
            else -> {
                postRequest.upJson(requestMsg)
            }
        }

        if (needToast) mCountDownHelper?.start()
        postRequest.execute(object : SimpleCallBack<String>() {
            override fun onError(e: ApiException) {
                XToastUtils.error(e.displayMessage)
                if (needToast) mCountDownHelper?.finish()
            }

            override fun onSuccess(response: String) {
                Log.i(TAG, response)
                try {
                    var json = response
                    if (HttpServerUtils.clientSafetyMeasures == 2) {
                        val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
                        json = RSACrypt.decryptByPublicKey(json, publicKey)
                        json = String(Base64.decode(json))
                    } else if (HttpServerUtils.clientSafetyMeasures == 3) {
                        val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
                        val encryptCBC = ConvertTools.hexStringToByteArray(json)
                        val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
                        json = String(decryptCBC)
                    }
                    val resp: BaseResponse<ConfigData> = Gson().fromJson(json, object : TypeToken<BaseResponse<ConfigData>>() {}.type)
                    if (resp.code == 200) {
                        serverConfig = resp.data!!
                        if (needToast) XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
                        //删除3.0.8之前保存的记录
                        serverHistory.remove(HttpServerUtils.serverAddress)
                        //添加到历史记录
                        val key = "【${serverConfig?.extraDeviceMark}】${HttpServerUtils.serverAddress}"
                        if (TextUtils.isEmpty(HttpServerUtils.clientSignKey)) {
                            serverHistory[key] = "SMSFORWARDER##" + HttpServerUtils.clientSafetyMeasures.toString()
                        } else {
                            serverHistory[key] = HttpServerUtils.clientSignKey + "##" + HttpServerUtils.clientSafetyMeasures.toString()
                        }
                        HttpServerUtils.serverHistory = Gson().toJson(serverHistory)
                        HttpServerUtils.serverConfig = Gson().toJson(serverConfig)
                    } else {
                        if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
                    }
                    if (needToast) mCountDownHelper?.finish()
                } catch (e: Exception) {
                    e.printStackTrace()
                    if (needToast) {
                        XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
                        mCountDownHelper?.finish()
                    }
                }
            }
        })
    }

    override fun onDestroyView() {
        if (mCountDownHelper != null) mCountDownHelper!!.recycle()
        super.onDestroyView()
    }

}