package com.example.modifier.ui.utils

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Environment
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.core.content.ContextCompat
import androidx.core.content.FileProvider.getUriForFile
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import coil3.ImageLoader
import coil3.request.ImageRequest
import coil3.request.crossfade
import coil3.toBitmap
import com.example.modifier.BuildConfig
import com.example.modifier.Frida
import com.example.modifier.R
import com.example.modifier.Utils
import com.example.modifier.baseTag
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.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.downloadImage
import com.example.modifier.http.response.SysConfigResponse
import com.example.modifier.repo.AppPrefsRepo
import com.example.modifier.repo.AppStateRepo
import com.example.modifier.repo.SpoofedSimInfoRepo
import com.example.modifier.service.TaskRunner
import com.example.modifier.service.TaskRunner.Companion
import com.example.modifier.utils.clear
import com.example.modifier.utils.clearConv
import com.example.modifier.utils.killPhoneProcess
import com.example.modifier.utils.resetAll
import com.example.modifier.utils.shellRun
import com.example.modifier.utils.injectOTP
import com.example.modifier.utils.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 org.apache.commons.lang3.RandomStringUtils
import java.io.File

class UtilsFragment : Fragment() {
    companion object {
        private const val TAG = "$baseTag/UtilsFragment"
    }

    private lateinit var binding: FragmentUtilsBinding
    private val appStateRepo = AppStateRepo.instance
    private val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
    private val imageLoader by lazy {
        ImageLoader.Builder(requireContext())
            .crossfade(true)
            .build()
    }

    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 {
            onClear()
        }
        binding.btnStop.setOnClickListener {
            onStopClick()
        }
        binding.btnSend.setOnClickListener {
            Utils.makeLoadingButton(context, binding.btnSend)
            lifecycleScope.launch {
                delay(1000L)

                val otp = binding.etOtp.text.toString()
                withContext(Dispatchers.IO) {
                    injectOTP(otp)
                    spoofedSimInfoRepo.updateAvailable(available = true)
                    appStateRepo.resetSuccessNum()
                    appStateRepo.resetExecutedNum()
                }

                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 {
            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.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) {
                    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.get(
                        SysConfigApi.Id(
                            SysConfigApi(),
                            "modifier_apk"
                        )
                    )
                        .body<SysConfigResponse>()
                    installApk(config.value)
                } catch (e: Exception) {
                    Log.e(TAG, "Failed to get message apk", e)
                }
            }
        }

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

        binding.btnUpdateGms.setOnClickListener {
            lifecycleScope.launch {
                try {
                    val config = ktorClient.get(
                        SysConfigApi.Id(
                            SysConfigApi(),
                            "gms_apk"
                        )
                    )
                        .body<SysConfigResponse>()
                    installApk(config.value)
                } catch (e: Exception) {
                    Log.e(TAG, "Failed to get gms apk", e)
                }
            }
        }

        binding.btnSendImg.setOnClickListener {
            lifecycleScope.launch {
                withContext(Dispatchers.IO) {
                    val file =
                        downloadImage("https://nebuai.oss-cn-hangzhou.aliyuncs.com/image/20241112/l57qnhga.jpg")
                    Log.i(TAG, "file: ${file.path}")
                    val intent = Intent(Intent.ACTION_SENDTO)
                    intent.data = Uri.parse("sms:+18583199738")
                    intent.putExtra("exit_on_sent", true)
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    intent.setPackage(PACKAGE_MESSAGING)

                    val contentUri: Uri =
                        getUriForFile(
                            com.example.modifier.utils.getContext(),
                            "${BuildConfig.APPLICATION_ID}.fileprovider",
                            file
                        )
                    intent.putExtra(Intent.EXTRA_STREAM, contentUri)
                    Log.i(TAG, "contentUri: ${contentUri}")
                    requireContext().grantUriPermission(
                        PACKAGE_MESSAGING,
                        contentUri,
                        Intent.FLAG_GRANT_READ_URI_PERMISSION
                    )
                    startActivity(intent)
                }
            }
        }

        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(
                    "temp",
                    ".apk",
                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                )
                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(TAG, "A file saved to ${file.path}")
                val installPath = "/data/local/tmp/${RandomStringUtils.randomAlphabetic(8)}.apk"
                shellRun(
                    "cp ${file.path} $installPath",
                    "pm install -d -r $installPath",
                    "rm -f $installPath"
                )
            } catch (e: Exception) {
                Log.e(TAG, "Failed to download apk", e)

            }

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