package br.gov.extrema.agenteti.device

import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.BatteryManager
import android.os.Build
import android.os.Environment
import android.os.StatFs
import android.provider.Settings
import br.gov.extrema.agenteti.BuildConfig
import br.gov.extrema.agenteti.security.SecureStore
import org.json.JSONArray
import org.json.JSONObject
import java.net.Inet4Address
import java.net.NetworkInterface
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

object DeviceCollector {
    fun sha256(value: String): String = MessageDigest.getInstance("SHA-256")
        .digest(value.toByteArray()).joinToString("") { "%02x".format(it) }

    private fun networkType(context: Context): String {
        val manager = context.getSystemService(ConnectivityManager::class.java)
        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork) ?: return "SEM CONEXÃO"
        return when {
            capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "WI-FI"
            capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "REDE MÓVEL"
            capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "ETHERNET"
            else -> "OUTRA"
        }
    }

    private fun localIp(): String = try {
        NetworkInterface.getNetworkInterfaces().toList().flatMap { it.inetAddresses.toList() }
            .firstOrNull { !it.isLoopbackAddress && it is Inet4Address }?.hostAddress.orEmpty()
    } catch (_: Exception) { "" }

    private fun battery(context: Context): JSONObject {
        val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) ?: return JSONObject()
        val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
        val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)
        val status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
        val plug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
        val charging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL
        return JSONObject().put("percent", if (scale > 0) (level * 100 / scale).coerceIn(0, 100) else 0)
            .put("charging", charging).put("temperature", intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) / 10.0)
            .put("status", when (status) { BatteryManager.BATTERY_STATUS_CHARGING -> "CARREGANDO"; BatteryManager.BATTERY_STATUS_FULL -> "CHEIA"; BatteryManager.BATTERY_STATUS_DISCHARGING -> "DESCARREGANDO"; else -> "N/I" })
            .put("source", when (plug) { BatteryManager.BATTERY_PLUGGED_AC -> "TOMADA"; BatteryManager.BATTERY_PLUGGED_USB -> "USB"; BatteryManager.BATTERY_PLUGGED_WIRELESS -> "SEM FIO"; else -> "BATERIA" })
    }

    private fun trackedApps(context: Context, store: SecureStore): JSONArray {
        val output = JSONArray();val catalog = JSONArray(store.get("tracked_apps", "[]"));val manager = context.packageManager
        for (index in 0 until catalog.length()) {
            val configured = catalog.optJSONObject(index) ?: continue
            val packageName = configured.optString("package_name");if (packageName.isBlank()) continue
            val item = JSONObject().put("package", packageName).put("label", configured.optString("label", packageName)).put("installed", false).put("version_name", "").put("version_code", 0).put("system_app", false)
            try {
                val info = manager.getPackageInfo(packageName, 0);val application = info.applicationInfo
                item.put("installed", true).put("version_name", info.versionName.orEmpty()).put("version_code", if (Build.VERSION.SDK_INT >= 28) info.longVersionCode else @Suppress("DEPRECATION") info.versionCode.toLong())
                    .put("system_app", application != null && application.flags and ApplicationInfo.FLAG_SYSTEM != 0)
                if (application != null) item.put("label", manager.getApplicationLabel(application).toString())
            } catch (_: PackageManager.NameNotFoundException) { }
            output.put(item)
        }
        return output
    }

    fun collect(context: Context, includeApps: Boolean): JSONObject {
        val store = SecureStore(context);val androidId = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID).orEmpty()
        val stableInput = "$androidId|${Build.MANUFACTURER}|${Build.MODEL}|${Build.PRODUCT}"
        val activity = context.getSystemService(ActivityManager::class.java);val memory = ActivityManager.MemoryInfo().also(activity::getMemoryInfo)
        val storage = StatFs(Environment.getDataDirectory().absolutePath);val total = storage.blockSizeLong * storage.blockCountLong / 1048576;val free = storage.blockSizeLong * storage.availableBlocksLong / 1048576
        val metrics = context.resources.displayMetrics;val battery = battery(context)
        return JSONObject()
            .put("fingerprint_hash", sha256(stableInput)).put("android_id_hash", sha256(androidId))
            .put("device_type", if (context.resources.configuration.smallestScreenWidthDp >= 600) "TABLET" else "SMARTPHONE")
            .put("manufacturer", Build.MANUFACTURER).put("brand", Build.BRAND).put("model", Build.MODEL).put("device_name", Build.DEVICE)
            .put("android_version", Build.VERSION.RELEASE).put("sdk_int", Build.VERSION.SDK_INT).put("security_patch", Build.VERSION.SECURITY_PATCH)
            .put("build_id", Build.ID).put("build_fingerprint", Build.FINGERPRINT).put("architecture", Build.SUPPORTED_ABIS.joinToString(","))
            .put("cpu_cores", Runtime.getRuntime().availableProcessors()).put("ram_total_mb", memory.totalMem / 1048576).put("ram_available_mb", memory.availMem / 1048576)
            .put("storage_total_mb", total).put("storage_free_mb", free).put("battery_percent", battery.optInt("percent"))
            .put("battery_charging", battery.optBoolean("charging")).put("battery_temperature", battery.optDouble("temperature"))
            .put("battery_status", battery.optString("status")).put("battery_source", battery.optString("source"))
            .put("network_type", networkType(context)).put("local_ip", localIp()).put("screen_resolution", "${metrics.widthPixels}x${metrics.heightPixels}")
            .put("screen_density", "${metrics.densityDpi} dpi").put("app_version", BuildConfig.VERSION_NAME).put("app_version_code", BuildConfig.VERSION_CODE)
            .put("patrimonio", store.get("patrimonio")).put("responsavel_nome", store.get("responsavel")).put("setor", store.get("setor"))
            .put("collected_at", SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(Date()))
            .also { if (includeApps) it.put("apps", trackedApps(context, store)) }
    }
}
