package com.example.modifier.ui.settings

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Bundle
import android.telephony.SmsManager
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.text.Editable
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.example.modifier.Global
import com.example.modifier.Global.save
import com.example.modifier.Global.servers
import com.example.modifier.R
import com.example.modifier.Utils
import com.example.modifier.data.AppPreferences
import com.example.modifier.data.AppPreferencesRepository
import com.example.modifier.databinding.FragmentSettingsBinding
import com.example.modifier.http.ktorClient
import com.example.modifier.http.api.RcsNumberApi
import com.example.modifier.http.request.RcsNumberRequest
import com.example.modifier.http.response.RcsNumberResponse
import com.example.modifier.model.TelephonyConfig
import com.example.modifier.service.ModifierService.Companion.instance
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.ktor.client.call.body
import io.ktor.client.plugins.resources.put
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.contentType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.commons.lang3.RandomStringUtils
import org.apache.commons.lang3.StringUtils
import org.apache.commons.validator.routines.UrlValidator
import java.util.Objects
import java.util.Optional

@SuppressLint("SetTextI18n", "MissingPermission", "HardwareIds", "NewApi")
class SettingsFragment : Fragment() {
    private val TAG = "SettingsFragment"

    private lateinit var binding: FragmentSettingsBinding
    private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
    private val appPreferencesRepository: AppPreferencesRepository by lazy {
        AppPreferencesRepository(requireContext())
    }
    private lateinit var appPreferences: StateFlow<AppPreferences>

    init {
        Log.i("SettingsFragment", "SettingsFragment")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        CoroutineScope(Dispatchers.IO).launch {
            appPreferences = appPreferencesRepository.getAppPreferences()
        }
        requestPermissionLauncher =
            registerForActivityResult(
                ActivityResultContracts.RequestPermission()
            ) { isGranted: Boolean ->
                if (isGranted) {
                } else {
                    // Explain to the user that the feature is unavailable because the
                    // feature requires a permission that the user has denied. At the
                    // same time, respect the user's decision. Don't link to system
                    // settings in an effort to convince the user to change their
                    // decision.
                }
            }
    }

    override fun onResume() {
        super.onResume()
        lifecycleScope.launch {
            loadConfigs()
        }

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        if (this::binding.isInitialized) {
            return binding.root
        }
        binding = FragmentSettingsBinding.inflate(inflater, container, false)
        binding.tlIccid.setEndIconOnClickListener {
            if (binding.etMcc.text.isNullOrEmpty() || binding.etMnc.text.isNullOrEmpty() || binding.etAreaCode.text.isNullOrEmpty()) {
                Toast.makeText(context, "MNC and Code required", Toast.LENGTH_SHORT).show()
                return@setEndIconOnClickListener
            }
            binding.etIccid.setText(
                Global.genICCID(
                    binding.etMnc.text.toString(),
                    binding.etAreaCode.text.toString()
                )
            )
        }
        binding.tlImsi.setEndIconOnClickListener {
            val mcc = Optional.ofNullable(
                binding.etMcc.text
            ).map { o: Editable? -> Objects.toString(o) }.orElse("")
            val mnc = Optional.ofNullable(binding.etMnc.text)
                .map { o: Editable? -> Objects.toString(o) }
                .orElse("")
            if (StringUtils.isEmpty(mcc) || StringUtils.isEmpty(mnc)) {
                Toast.makeText(context, "MCC and MNC are required", Toast.LENGTH_SHORT).show()
                return@setEndIconOnClickListener
            }
            binding.etImsi.setText(mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length))
        }
        binding.tlImei.setEndIconOnClickListener {
            binding.etImei.setText(Utils.generateIMEI())
        }
        binding.btnSave.setOnClickListener {
            onSave()
        }
        binding.etServer.threshold = 1000
        binding.btnServer.setOnClickListener { v: View? ->
            val server = binding.etServer.text.toString()
            if (StringUtils.isEmpty(server)) {
                Toast.makeText(context, "Server is required", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            if (!UrlValidator(arrayOf("http", "https")).isValid(server)) {
                Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            binding.etServer.setSimpleItems(servers.toTypedArray<String>())
            lifecycleScope.launch {
                appPreferencesRepository.updateServer(server)
                appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
                instance?.connect()
                Utils.makeLoadingButton(context, binding.btnServer)
                binding.btnServer.isEnabled = false
                delay(500)
                binding.btnServer.setIconResource(R.drawable.ic_done)
                binding.btnServer.text = "OK"
                delay(500)
                binding.btnServer.isEnabled = true
                binding.btnServer.icon = null
                binding.btnServer.text = "Save"
            }
        }

        binding.btnRequest.setOnClickListener { v: View? ->
            lifecycleScope.launch {
                Utils.makeLoadingButton(context, binding.btnRequest)
                binding.btnRequest.isEnabled = false

                withContext(Dispatchers.IO) req@{
                    val response: HttpResponse
                    try {
                        response = ktorClient(appPreferences.value.server).put(
                            RcsNumberApi()
                        ) {
                            contentType(ContentType.Application.Json)
                            setBody(
                                RcsNumberRequest(
                                    deviceId = Utils.getUniqueID()
                                )
                            )
                        }
                    } catch (e: Exception) {
                        withContext(Dispatchers.Main) {
                            MaterialAlertDialogBuilder(requireContext())
                                .setTitle("Error")
                                .setMessage(e.message)
                                .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
                                    dialog.dismiss()
                                }
                                .show()
                            binding.btnRequest.isEnabled = true
                            binding.btnRequest.text = "Request"
                            binding.btnRequest.icon = null
                        }
                        return@req
                    }
                    Log.i(TAG, "response: ${response.bodyAsText()}")
                    val res = response.body<RcsNumberResponse>()
                    val number = res.number
                    val mcc = res.mcc
                    val mnc = res.mnc
                    val country = res.country
                    val areaCode = res.areaCode
                    val iccid = Global.genICCID(mnc, areaCode)
                    val imsi =
                        mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
                    val imei = Utils.generateIMEI()
                    save(
                        TelephonyConfig(
                            number,
                            mcc,
                            mnc,
                            iccid,
                            imsi,
                            imei,
                            country,
                            areaCode,
                            false,
                            res.carrierId,
                            res.carrierName
                        )
                    )

                    loadConfigs()
                }
                binding.btnRequest.icon = null
                binding.btnRequest.isEnabled = true
                binding.btnRequest.text = "Request"
            }
        }

        binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
            CoroutineScope(Dispatchers.IO).launch {
                appPreferencesRepository.updatePreventClean(isChecked)
            }
        }
        binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
            CoroutineScope(Dispatchers.IO).launch {
                appPreferencesRepository.updatePreventRequest(isChecked)
            }
        }
        binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
            CoroutineScope(Dispatchers.IO).launch {
                appPreferencesRepository.updatePreventReset(isChecked)
            }
        }

        binding.btnCheck.setOnClickListener {

            when {
                ContextCompat.checkSelfPermission(
                    requireContext(),
                    Manifest.permission.READ_PHONE_STATE
                ) == PackageManager.PERMISSION_GRANTED -> {
                    val telephonyManager =
                        requireContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

                    var res = """IMEI: ${telephonyManager.imei}
                    |IMSI: ${telephonyManager.subscriberId}
                    |ICCID: ${telephonyManager.simSerialNumber}
                    |Phone Number: ${telephonyManager.line1Number}
                    |Network Operator: ${telephonyManager.networkOperator}, ${telephonyManager.networkCountryIso}, ${telephonyManager.networkOperatorName}
                    |Sim Operator: ${telephonyManager.simOperator}, ${telephonyManager.simCountryIso}, ${telephonyManager.simOperatorName}
                    |Sim state: ${telephonyManager.simState}
                    |simCarrierId: ${telephonyManager.simCarrierId}
                    |carrierIdFromSimMccMnc: ${telephonyManager.carrierIdFromSimMccMnc}
                    |simSpecificCarrierId: ${telephonyManager.simSpecificCarrierId}
                    |DefaultSmsSubscriptionId: ${SmsManager.getDefaultSmsSubscriptionId()}
                    |
                    """.trimMargin()
                    val subscriptionManager: SubscriptionManager =
                        requireContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager

                    val simCount = subscriptionManager.activeSubscriptionInfoCountMax
                    for (i in 0 until simCount) {
                        val info =
                            subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i)
                                ?: continue
                        res += """
                        |--- Slot:$i Subscription ID: ${info.subscriptionId} ---
                        |ICCID: ${info.iccId}
                        |Number: ${subscriptionManager.getPhoneNumber(info.subscriptionId)}
                        |Country: ${info.countryIso}
                        |MCC: ${info.mccString}
                        |MNC: ${info.mncString}
                        |carrierId: ${info.carrierId}
                        |roaming: ${info.dataRoaming}
                        """.trimMargin()
                    }
                    MaterialAlertDialogBuilder(requireContext())
                        .setTitle("Info")
                        .setMessage(res)
                        .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
                            dialog.dismiss()
                        }
                        .show()
                }

                ActivityCompat.shouldShowRequestPermissionRationale(
                    requireActivity(), Manifest.permission.READ_PHONE_STATE
                ) -> {
                    // In an educational UI, explain to the user why your app requires this
                    // permission for a specific feature to behave as expected, and what
                    // features are disabled if it's declined. In this UI, include a
                    // "cancel" or "no thanks" button that lets the user continue
                    // using your app without granting the permission.
                }

                else -> {
                    // You can directly ask for the permission.
                    // The registered ActivityResultCallback gets the result of this request.
                    requestPermissionLauncher.launch(
                        Manifest.permission.READ_PHONE_STATE
                    )
                }
            }

        }
        lifecycleScope.launch {
            loadConfigs()
            appPreferencesRepository.getAppPreferences().collect {
                binding.etServer.setText(it.server)
                binding.etDeviceLabel.setText(it.name)
                binding.switchClean.isChecked = it.preventClean
                binding.switchRequest.isChecked = it.preventRequest
                binding.swReset.isChecked = it.preventReset
            }
        }

        return binding.root
    }

    private suspend fun loadConfigs() {
        withContext(Dispatchers.IO) {
            Global.load()
            withContext(Dispatchers.Main) {
                binding.etServer.setSimpleItems(servers.toTypedArray<String>())
                val telephonyConfig = Global.telephonyConfig
                binding.etNumber.setText(telephonyConfig.number)
                binding.etMcc.setText(telephonyConfig.mcc)
                binding.etMnc.setText(telephonyConfig.mnc)
                binding.etIccid.setText(telephonyConfig.iccid)
                binding.etImsi.setText(telephonyConfig.imsi)
                binding.etImei.setText(telephonyConfig.imei)
                binding.etCountry.setText(telephonyConfig.country)
                binding.etAreaCode.setText(telephonyConfig.areaCode)
                binding.etCarrierId.setText(telephonyConfig.carrierId)
                binding.etCarrierName.setText(telephonyConfig.carrierName)
            }
        }
    }

    private fun onSave() {
        Utils.makeLoadingButton(context, binding.btnSave)
        binding.btnSave.isEnabled = false
        lifecycleScope.launch {
            withContext(Dispatchers.IO) {
                save(
                    TelephonyConfig(
                        binding.etNumber.text.toString(),
                        binding.etMcc.text.toString(),
                        binding.etMnc.text.toString(),
                        binding.etIccid.text.toString(),
                        binding.etImsi.text.toString(),
                        binding.etImei.text.toString(),
                        binding.etCountry.text.toString(),
                        binding.etAreaCode.text.toString(),
                        false,
                        binding.etCarrierId.text.toString(),
                        binding.etCarrierName.text.toString()
                    )
                )
            }
            binding.btnSave.setIconResource(R.drawable.ic_done)
            binding.btnSave.text = "OK"
            delay(1500)
            binding.btnSave.isEnabled = true
            binding.btnSave.icon = null
            binding.btnSave.text = "Save"
        }
    }
}