package com.example.modifier.ui.utils

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import com.example.modifier.CMD_BACK_APP
import com.example.modifier.Frida
import com.example.modifier.Global
import com.example.modifier.Global.clear
import com.example.modifier.Global.clearConv
import com.example.modifier.Global.resetAll
import com.example.modifier.Global.suspend
import com.example.modifier.Global.unsuspend
import com.example.modifier.R
import com.example.modifier.Utils
import com.example.modifier.databinding.DialogUpdateBinding
import com.example.modifier.databinding.FragmentUtilsBinding
import com.example.modifier.http.KtorClient
import com.example.modifier.http.api.SysConfigApi
import com.example.modifier.http.response.SysConfigResponse
import com.example.modifier.service.ModifierService
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.dialog.MaterialDialogs
import com.google.android.material.progressindicator.CircularProgressIndicatorSpec
import com.google.android.material.progressindicator.DeterminateDrawable
import com.google.android.material.progressindicator.IndeterminateDrawable
import io.ktor.client.call.body
import io.ktor.client.plugins.onDownload
import io.ktor.client.plugins.resources.get
import io.ktor.client.request.get
import io.ktor.client.request.prepareGet
import io.ktor.http.contentLength
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.isEmpty
import io.ktor.utils.io.core.readBytes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class UtilsFragment : Fragment() {
    private lateinit var binding: FragmentUtilsBinding

    var handler: Handler = Handler(Looper.getMainLooper())
    var executor: ExecutorService = Executors.newFixedThreadPool(4)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        if (this::binding.isInitialized) {
            return binding.root
        }
        binding = FragmentUtilsBinding.inflate(inflater, container, false)
        binding.btnClear.setOnClickListener { v: View? ->
            onClear()
        }
        binding.btnStop.setOnClickListener { v: View? ->
            onStopClick()
        }
        binding.btnSend.setOnClickListener { v: View? ->
            Utils.makeLoadingButton(context, binding.btnSend)
            CoroutineScope(Dispatchers.Main).launch {
                delay(1000L)

                val otp = binding.etOtp.text.toString()
                withContext(Dispatchers.IO) {
                    Global.sendSmsIntent("3538", "Your Messenger verification code is G-$otp")
                }

                binding.btnSend.setIconResource(R.drawable.ic_done)
                binding.btnSend.text = "OK"

                delay(1500L)

                binding.btnSend.isEnabled = true
                binding.btnSend.icon = null
                binding.btnSend.text = "Send"
            }
        }
        binding.btnClearConv.setOnClickListener { v: View? ->
            binding.btnClearConv.isEnabled = false
            Utils.makeLoadingButton(context, binding.btnClearConv)
            CoroutineScope(Dispatchers.IO).launch {
                clearConv()
                withContext(Dispatchers.Main) {
                    binding.btnClearConv.setIconResource(R.drawable.ic_done)
                    binding.btnClearConv.text = "OK"
                    delay(1500L)
                    binding.btnClearConv.isEnabled = true
                    binding.btnClearConv.icon = null
                    binding.btnClearConv.text = "Clear Msg"
                }
            }
        }

        binding.btnCheckA10y.setOnClickListener {
            Utils.makeLoadingButton(context, binding.btnCheckA10y)
            CoroutineScope(Dispatchers.Main).launch {
                var a10y: Boolean? = null
                withContext(Dispatchers.IO) {
                    a10y = ModifierService.instance?.checkRcsAvailability()
                    Utils.runAsRoot(CMD_BACK_APP)
                }
                if (isAdded) {
                    MaterialAlertDialogBuilder(requireContext())
                        .setMessage("RCS is ${if (a10y == true) "available" else "not available"}")
                        .setPositiveButton("OK") { _, _ -> }
                        .show()
                }

                binding.btnCheckA10y.setIconResource(R.drawable.ic_done)
                binding.btnCheckA10y.text = "OK"
                delay(1500L)
                binding.btnCheckA10y.isEnabled = true
                binding.btnCheckA10y.icon = null
                binding.btnCheckA10y.text = "Check A10y"
            }
        }

        binding.btnSuspend.setOnClickListener {
            binding.btnSuspend.isEnabled = false
            Utils.makeLoadingButton(context, binding.btnSuspend)
            executor.execute {
                val gsf = binding.cbGsf.isChecked
                val gms = binding.cbGms.isChecked
                val sms = binding.cbSms.isChecked
                suspend(gsf, gms, sms)
                handler.post {
                    binding.btnSuspend.setIconResource(R.drawable.ic_done)
                    binding.btnSuspend.text = "OK"
                    handler.postDelayed({
                        binding.btnSuspend.isEnabled = true
                        binding.btnSuspend.icon = null
                        binding.btnSuspend.text = "Suspend"
                    }, 1500)
                }
            }
        }
        binding.btnUnsuspend.setOnClickListener {
            binding.btnUnsuspend.isEnabled = false
            Utils.makeLoadingButton(context, binding.btnUnsuspend)
            executor.execute {
                val gsf = binding.cbGsf.isChecked
                val gms = binding.cbGms.isChecked
                val sms = binding.cbSms.isChecked
                unsuspend(gsf, gms, sms)
                handler.post {
                    binding.btnUnsuspend.setIconResource(R.drawable.ic_done)
                    binding.btnUnsuspend.text = "OK"
                    handler.postDelayed({
                        binding.btnUnsuspend.isEnabled = true
                        binding.btnUnsuspend.icon = null
                        binding.btnUnsuspend.text = "Unsuspend"
                    }, 1500)
                }
            }
        }

        binding.btnStartFrida.setOnClickListener {
            CoroutineScope(Dispatchers.Main).launch {
                withContext(Dispatchers.IO) {
                    Frida.start()
                }
            }
            Toast.makeText(context, "Frida started", Toast.LENGTH_SHORT).show()
        }

        binding.btnStopFrida.setOnClickListener {
            CoroutineScope(Dispatchers.Main).launch {
                withContext(Dispatchers.IO) {
                    Frida.stop()
                }
            }
            Toast.makeText(context, "Frida stopped", Toast.LENGTH_SHORT).show()
        }

        binding.btnKillPhone.setOnClickListener {
            binding.btnKillPhone.isEnabled = false
            Utils.makeLoadingButton(context, binding.btnKillPhone)
            CoroutineScope(Dispatchers.IO).launch {
                val success = Global.killPhoneProcess(force = true)
                withContext(Dispatchers.Main) {
                    if (success) {
                        binding.btnKillPhone.setIconResource(R.drawable.ic_done)
                        binding.btnKillPhone.text = "OK"
                    } else {
                        binding.btnKillPhone.setIconResource(R.drawable.ic_error)
                        binding.btnKillPhone.text = "Fail"
                    }
                    delay(1500L)
                    binding.btnKillPhone.isEnabled = true
                    binding.btnKillPhone.icon = null
                    binding.btnKillPhone.text = "Kill Phone"
                }
            }
        }

        binding.btnSyncTime.setOnClickListener {
            binding.btnSyncTime.isEnabled = false
            Utils.makeLoadingButton(context, binding.btnSyncTime)
            CoroutineScope(Dispatchers.IO).launch {
                Global.syncTime()
                withContext(Dispatchers.Main) {
                    binding.btnSyncTime.setIconResource(R.drawable.ic_done)
                    binding.btnSyncTime.text = "OK"
                    delay(1500L)
                    binding.btnSyncTime.isEnabled = true
                    binding.btnSyncTime.icon = null
                    binding.btnSyncTime.text = "Sync Time"
                }
            }
        }

        binding.btnUpdateModifier.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                try {
                    val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "modifier_apk"))
                        .body<SysConfigResponse>()
                    installApk(config.value)
                } catch (e: Exception) {
                    Log.e("Modifier", "Failed to get message apk", e)
                }
            }
        }

        binding.btnUpdateMessage.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                try {
                    val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "message_apk"))
                        .body<SysConfigResponse>()
                    installApk(config.value)
                } catch (e: Exception) {
                    Log.e("Modifier", "Failed to get message apk", e)
                }
            }
        }

        return binding.root
    }

    private fun onClear() {
        binding.btnClear.isEnabled = false
        Utils.makeLoadingButton(context, binding.btnClear)
        val all = binding.cbAll.isChecked
        val gsf = binding.cbGsf.isChecked
        val gms = binding.cbGms.isChecked
        val sms = binding.cbSms.isChecked
        CoroutineScope(Dispatchers.IO).launch {
            if (all) {
                resetAll()
            } else {
                clear(gsf, gms, sms)
            }
            withContext(Dispatchers.Main) {
                binding.btnClear.setIconResource(R.drawable.ic_done)
                binding.btnClear.text = "OK"
                delay(1500)
                binding.btnClear.isEnabled = true
                binding.btnClear.icon = null
                binding.btnClear.text = "Clear"
            }
        }

    }

    private fun onStopClick() {
        binding.btnStop.isEnabled = false
        Utils.makeLoadingButton(context, binding.btnStop)
        executor.execute {
            val gsf = binding.cbGsf.isChecked
            val gms = binding.cbGms.isChecked
            val sms = binding.cbSms.isChecked
            suspend(gsf, gms, sms)
            unsuspend(gsf, gms, sms)
            handler.post {
                binding.btnStop.setIconResource(R.drawable.ic_done)
                binding.btnStop.text = "OK"
                handler.postDelayed({
                    binding.btnStop.isEnabled = true
                    binding.btnStop.icon = null
                    binding.btnStop.text = "Stop"
                }, 1500)
            }
        }
    }

    private suspend fun installApk(url: String) {
        if (url.isEmpty()) {
            Toast.makeText(context, "URL is empty", Toast.LENGTH_SHORT).show()
            return
        }
        if (!url.endsWith(".apk")) {
            Toast.makeText(context, "URL is not an APK", Toast.LENGTH_SHORT).show()
            return
        }
        if (!url.startsWith("http")) {
            Toast.makeText(context, "URL is not valid", Toast.LENGTH_SHORT).show()
            return
        }
        var job: Job? = null
        lateinit var dialogBinding: DialogUpdateBinding
        lateinit var dialog: AlertDialog
        withContext(Dispatchers.Main) {
            dialogBinding = DialogUpdateBinding.inflate(layoutInflater)
            dialog = MaterialAlertDialogBuilder(requireContext())
                .setTitle("Installing...")
                .setView(dialogBinding.root)
                .setCancelable(false)
                .setNegativeButton("Cancel") { _, _ ->
                    job?.cancel()
                }
                .create()
            dialog.show()
        }

        job = CoroutineScope(Dispatchers.IO).launch {
            try {
                val file = File.createTempFile("files", ".apk")

                KtorClient.prepareGet(url)
                    .execute { httpResponse ->
                        val channel: ByteReadChannel = httpResponse.body()
                        while (!channel.isClosedForRead) {
                            val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
                            while (!packet.isEmpty) {
                                val bytes = packet.readBytes()
                                file.appendBytes(bytes)
                                withContext(
                                    Dispatchers.Main
                                ) {
                                    dialogBinding.progressBar.isIndeterminate = false
                                    dialogBinding.progressBar.progress =
                                        (file.length() * 100 / httpResponse.contentLength()!!).toInt()
                                }
                            }
                        }
                    }
                Log.i("Modifier", "A file saved to ${file.path}")
                Utils.runAsRoot("pm install -d -r ${file.path}")
            } catch (e: Exception) {
                Log.e("Modifier", "Failed to download apk", e)

            }

            withContext(Dispatchers.Main) {
                dialog.dismiss()
            }
        }
    }
}