package partisan.weforge.xyz.pulse

import android.Manifest
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.Uri
import android.util.Log
import android.provider.ContactsContract
import android.telecom.CallRedirectionService
import android.telecom.PhoneAccountHandle
import android.telephony.TelephonyManager
import androidx.annotation.RequiresPermission
import com.google.i18n.phonenumbers.PhoneNumberUtil
import java.lang.ref.WeakReference

class CallRedirectionService : CallRedirectionService() {
    companion object {
        private const val PREFIX = "vnd.android.cursor.item"
        private const val SIGNAL_MIMETYPE = "$PREFIX/vnd.org.thoughtcrime.securesms.call"
        private const val TELEGRAM_MIMETYPE = "$PREFIX/vnd.org.telegram.messenger.android.call"
        private const val THREEMA_MIMETYPE = "$PREFIX/vnd.ch.threema.app.call"
        private const val WHATSAPP_MIMETYPE = "$PREFIX/vnd.com.whatsapp.voip.call"
    
        val ALL_MIMETYPES = arrayOf(
            SIGNAL_MIMETYPE,
            TELEGRAM_MIMETYPE,
            THREEMA_MIMETYPE,
            WHATSAPP_MIMETYPE,
        )
    
        private val MIMETYPE_TO_DST_NAME = mapOf(
            SIGNAL_MIMETYPE to R.string.destination_signal,
            TELEGRAM_MIMETYPE to R.string.destination_telegram,
            THREEMA_MIMETYPE to R.string.destination_threema,
            WHATSAPP_MIMETYPE to R.string.destination_whatsapp,
        )
    }

    private lateinit var prefs: Preferences
    private lateinit var window: PopupWindow
    private var connectivityManager: ConnectivityManager? = null

    override fun onCreate() {
        super.onCreate()
        init()
    }

    override fun onDestroy() {
        super.onDestroy()
        window.cancel()
    }

    private fun init() {
        prefs = Preferences(this)
        window = PopupWindow(this, WeakReference(this))
        connectivityManager = getSystemService(ConnectivityManager::class.java)
    }

    override fun onPlaceCall(
        handle: Uri,
        initialPhoneAccount: PhoneAccountHandle,
        allowInteractiveResponse: Boolean,
    ) {
        val phoneNumber = handle.schemeSpecificPart
        val numberAlias = getAnonymizedAlias(phoneNumber)
        Log.d("Redirection", "onPlaceCall triggered: alias=$numberAlias, interactive=$allowInteractiveResponse")

        val capabilities = connectivityManager
            ?.getNetworkCapabilities(connectivityManager?.activeNetwork)

        val isWifi = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
        val isCellular = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true
        Log.d("Redirection", "isWifi=$isWifi, isCellular=$isCellular")

        val shouldRedirect = when {
            isWifi && !prefs.redirectOnWifi -> false
            isCellular && !prefs.redirectOnData -> false
            else -> true
        }

        if (!prefs.isEnabled) {
            Log.d("Redirection", "Aborting: redirection disabled in prefs")
            placeCallUnmodified()
            return
        }

        if (!shouldRedirect) {
            Log.d("Redirection", "Aborting: redirection blocked by current network preference")
            placeCallUnmodified()
            return
        }

        if (!hasInternet()) {
            Log.d("Redirection", "Aborting: no internet connection detected")
            placeCallUnmodified()
            return
        }

        if (prefs.redirectIfRoaming && !isOutsideHomeCountry()) {
            Log.d("Redirection", "Aborting: redirect only while roaming, but we're inside home country")
            placeCallUnmodified()
            return
        }

        if (prefs.redirectInternationalOnly && !isInternationalNumber(phoneNumber)) {
            Log.d("Redirection", "Aborting: number $numberAlias is not international and pref requires it")
            placeCallUnmodified()
            return
        }

        if (prefs.isBlacklistEnabled && !prefs.isContactWhitelisted(phoneNumber)) {
            Log.d("Redirection", "Aborting: number $numberAlias is not in whitelist while blacklist is enabled")
            placeCallUnmodified()
            return
        }
        
        Log.d("Redirection", "Number $numberAlias is not in filters, processing redirection...")

        val records: Array<Record>
        try {
            records = getRecordsFromPhoneNumber(phoneNumber)
           Log.d("Redirection", "Found ${records.size} raw redirect apps for number $numberAlias")
        } catch (exc: SecurityException) {
            Log.w("Redirection", "SecurityException during record fetch", exc)
            placeCallUnmodified()
            return
        }

        val enabledRecords = records
            .filter { prefs.isServiceEnabled(it.mimetype) }
            .sortedBy { prefs.getServicePriority(it.mimetype) }

        Log.d("Redirection", "Filtered to ${enabledRecords.size} enabled redirect apps")

        val record = enabledRecords.firstOrNull()
        if (record == null) {
            Log.d("Redirection", "Aborting: no suitable redirect apps found for number $numberAlias")
            placeCallUnmodified()
            return
        }

        Log.d("Redirection", "Redirecting call to: ${record.mimetype} → ${record.uri}")

        Log.d("Redirection", "Popup ${if (allowInteractiveResponse) "allowed" else "not allowed"} by system; ${if (prefs.popupEnabled) "enabled" else "disabled"} in prefs")

        if (allowInteractiveResponse && prefs.popupEnabled) {
            window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
        } else {
            window.call(record.uri)
            cancelCall()
        }
    }

    @RequiresPermission(Manifest.permission.READ_CONTACTS)
    private fun getContactIdByPhoneNumber(phoneNumber: String): String? {
        var result: String? = null
        val cursor = contentResolver.query(
            Uri.withAppendedPath(
                ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
                Uri.encode(phoneNumber),
            ),
            arrayOf(ContactsContract.PhoneLookup._ID),
            null,
            null,
            null,
        )
        cursor?.apply {
            if (moveToFirst())
                result = getString(getColumnIndexOrThrow(ContactsContract.PhoneLookup._ID))
            close()
        }
        return result
    }

    private data class Record(val uri: Uri, val mimetype: String)

    @RequiresPermission(Manifest.permission.READ_CONTACTS)
    private fun getRecordsFromPhoneNumber(phoneNumber: String): Array<Record> {
        val results = mutableSetOf<Record>()
        val contactId = getContactIdByPhoneNumber(phoneNumber) ?: return results.toTypedArray()
        val cursor = contentResolver.query(
            ContactsContract.Data.CONTENT_URI,
            arrayOf(ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE),
             "${ContactsContract.Data.CONTACT_ID} = ? AND " +
                     "${ContactsContract.Data.MIMETYPE} IN (${ALL_MIMETYPES.joinToString(",") { "?" }})",
            arrayOf(contactId, *ALL_MIMETYPES),
            null,
        )
        cursor?.apply {
            while (moveToNext())
                results.add(Record(
                    Uri.withAppendedPath(
                        ContactsContract.Data.CONTENT_URI,
                        Uri.encode(getString(getColumnIndexOrThrow(ContactsContract.Data._ID))),
                    ),
                    getString(getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)),
                ))
            close()
        }
        return results.toTypedArray()
    }

    private fun getAnonymizedAlias(number: String): String {
        val prefs = getSharedPreferences("anonymized_numbers", MODE_PRIVATE)

        // Return existing alias if already mapped
        val existing = prefs.getString(number, null)
        if (existing != null) return existing

        // Start from current counter
        var counter = prefs.getInt("counter", 1)
        var alias: String

        // Find the first unused alias (safety check)
        while (true) {
            alias = "#$counter"
            if (!prefs.all.containsValue(alias)) break
            counter++
        }

        // Store new alias and increment counter
        prefs.edit()
            .putString(number, alias)
            .putInt("counter", counter + 1)
            .apply()

        return alias
    }

    private fun isInternationalNumber(phoneNumber: String): Boolean {
        val telephony = getSystemService(TelephonyManager::class.java) ?: return true
        val simCountryIso = telephony.simCountryIso?.lowercase() ?: return true

        // Use libphonenumber to parse the number and get region
        val util = PhoneNumberUtil.getInstance()
        return try {
            val numberProto = util.parse(phoneNumber, simCountryIso.uppercase())
            val numberRegion = util.getRegionCodeForNumber(numberProto)?.lowercase()
            numberRegion != simCountryIso
        } catch (e: Exception) {
            true // treat as international if parsing fails
        }
    }

    fun isOutsideHomeCountry(): Boolean {
        val telephony = getSystemService(TelephonyManager::class.java) ?: return true

        val simCountry = telephony.simCountryIso?.lowercase()
        val networkCountry = telephony.networkCountryIso?.lowercase()

        // If SIM or network country can't be determined, assume we're abroad
        if (simCountry.isNullOrBlank() || networkCountry.isNullOrBlank()) return true

        // If they don't match, you're abroad
        return simCountry != networkCountry
    }

    @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
    private fun hasInternet(): Boolean { // This "hasInternet" func is (kinda) re-defined in Donation Fragment 
        val capabilities = connectivityManager
            ?.getNetworkCapabilities(connectivityManager?.activeNetwork) ?: return false
        return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
                capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
    }
}
