package com.example.modifier.ui.utils

import android.os.Bundle
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 androidx.lifecycle.lifecycleScope
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.resumePackage
import com.example.modifier.Global.suspendPackage
import com.example.modifier.R
import com.example.modifier.Utils
import com.example.modifier.constants.CMD_BACK_APP
import com.example.modifier.constants.PACKAGE_GMS
import com.example.modifier.constants.PACKAGE_GSF
import com.example.modifier.constants.PACKAGE_MESSAGING
import com.example.modifier.data.AppPreferences
import com.example.modifier.data.AppPreferencesRepository
import com.example.modifier.databinding.DialogUpdateBinding
import com.example.modifier.databinding.FragmentUtilsBinding
import com.example.modifier.extension.kill
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.example.modifier.shellRun
import com.example.modifier.syncTime
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.ktor.client.call.body
import io.ktor.client.plugins.resources.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.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File

class UtilsFragment : Fragment() {
    private lateinit var binding: FragmentUtilsBinding
    private val appPreferencesRepository: AppPreferencesRepository by lazy {
        AppPreferencesRepository(requireContext())
    }
    private lateinit var appPreferences: StateFlow<AppPreferences>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        CoroutineScope(Dispatchers.IO).launch {
            appPreferences = appPreferencesRepository.getAppPreferences()
        }
    }

    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)
            lifecycleScope.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)
            lifecycleScope.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)
            lifecycleScope.launch {
                var a10y: Boolean? = null
                withContext(Dispatchers.IO) {
                    a10y = ModifierService.instance?.checkRcsAvailability()
                    shellRun(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)
            lifecycleScope.launch {

                val packages = mutableListOf<String>()
                if (binding.cbGsf.isChecked) {
                    packages.add(PACKAGE_GSF)
                }
                if (binding.cbGms.isChecked) {
                    packages.add(PACKAGE_GMS)
                }
                if (binding.cbSms.isChecked) {
                    packages.add(PACKAGE_MESSAGING)
                }
                withContext(Dispatchers.IO) {
                    suspendPackage(*packages.toTypedArray())
                }
                binding.btnSuspend.setIconResource(R.drawable.ic_done)
                binding.btnSuspend.text = "OK"
                delay(1500)
                binding.btnSuspend.isEnabled = true
                binding.btnSuspend.icon = null
                binding.btnSuspend.text = "Suspend"
            }
        }
        binding.btnUnsuspend.setOnClickListener {
            binding.btnUnsuspend.isEnabled = false
            Utils.makeLoadingButton(context, binding.btnUnsuspend)
            lifecycleScope.launch {
                val packages = mutableListOf<String>()
                if (binding.cbGsf.isChecked) {
                    packages.add(PACKAGE_GSF)
                }
                if (binding.cbGms.isChecked) {
                    packages.add(PACKAGE_GMS)
                }
                if (binding.cbSms.isChecked) {
                    packages.add(PACKAGE_MESSAGING)
                }
                withContext(Dispatchers.IO) {
                    resumePackage(*packages.toTypedArray())
                }
                binding.btnUnsuspend.setIconResource(R.drawable.ic_done)
                binding.btnUnsuspend.text = "OK"
                delay(1500)
                binding.btnUnsuspend.isEnabled = true
                binding.btnUnsuspend.icon = null
                binding.btnUnsuspend.text = "Unsuspend"
            }
        }

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

        binding.btnStopFrida.setOnClickListener {
            lifecycleScope.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)
            lifecycleScope.launch {
                val success = withContext(Dispatchers.IO) {
                    Global.killPhoneProcess(force = true)
                }
                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)
            lifecycleScope.launch {
                withContext(Dispatchers.IO) {
                    syncTime()
                }
                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 {
            lifecycleScope.launch {
                try {
                    val config = ktorClient(appPreferences.value.server).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 {
            lifecycleScope.launch {
                try {
                    val config = ktorClient(appPreferences.value.server).get(SysConfigApi.Id(SysConfigApi(), "message_apk"))
                        .body<SysConfigResponse>()
                    installApk(config.value)
                } catch (e: Exception) {
                    Log.e("Modifier", "Failed to get message apk", e)
                }
            }
        }

        binding.btnUpdateGms.setOnClickListener {
            lifecycleScope.launch {
                try {
                    val config = ktorClient(appPreferences.value.server).get(SysConfigApi.Id(SysConfigApi(), "gms_apk"))
                        .body<SysConfigResponse>()
                    installApk(config.value)
                } catch (e: Exception) {
                    Log.e("Modifier", "Failed to get gms 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
        lifecycleScope.launch {
            withContext(Dispatchers.IO) {
                if (all) {
                    resetAll()
                } else {
                    clear(gsf, gms, sms)
                }
            }
            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)
        lifecycleScope.launch {
            withContext(Dispatchers.IO) {
                val packages = mutableListOf<String>()
                if (binding.cbGsf.isChecked) {
                    packages.add(PACKAGE_GSF)
                }
                if (binding.cbGms.isChecked) {
                    packages.add(PACKAGE_GMS)
                }
                if (binding.cbSms.isChecked) {
                    packages.add(PACKAGE_MESSAGING)
                }
                packages.forEach {
                    shellRun(it.kill())
                }
            }
            binding.btnStop.setIconResource(R.drawable.ic_done)
            binding.btnStop.text = "OK"
            delay(1500)
            binding.btnStop.isEnabled = true
            binding.btnStop.icon = null
            binding.btnStop.text = "Stop"
        }
    }

    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(appPreferences.value.server).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}")
                shellRun("pm install -d -r ${file.path}")
            } catch (e: Exception) {
                Log.e("Modifier", "Failed to download apk", e)

            }

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