package com.erwan.revwatch.prefs

import android.content.Context
import android.content.SharedPreferences
import com.erwan.revwatch.model.BridgeConfig
import com.erwan.revwatch.model.CellConfig
import com.erwan.revwatch.model.ConnectionMode
import com.erwan.revwatch.model.DashPage
import com.erwan.revwatch.model.GaugeRange
import com.erwan.revwatch.model.face.FaceStore
import com.erwan.revwatch.model.VehicleProfile
import com.erwan.revwatch.model.WidgetType

object AppPrefs {
    private const val PREFS_NAME = "obd_bridge_prefs"

    private const val KEY_SETUP_COMPLETE = "setup_complete"
    private const val KEY_CONNECTION_MODE = "connection_mode"
    private const val KEY_AUTO_RECONNECT = "auto_reconnect"
    private const val KEY_VEHICLE_PROFILE = "vehicle_profile"
    private const val KEY_PAGES = "dash_pages"
    private const val KEY_BT_MAC = "bt_mac"
    private const val KEY_CUSTOM_DPI = "custom_dpi"
    private const val KEY_SCREEN_CHARGE_CONTROL = "screen_charge_control"
    private const val KEY_HIDDEN_APPS = "hidden_apps"
    private const val KEY_APP_LABELS = "custom_app_labels"
    private const val KEY_APP_ICONS = "custom_app_icons"
    private const val KEY_WALLPAPER = "wallpaper"

    // Old keys for migration
    private const val KEY_WIDGETS = "widgets"
    private const val KEY_WIDGET_STYLES = "widget_styles"
    private const val KEY_WIDGET_UNITS = "widget_units"
    private const val KEY_WIDGET_RANGES = "widget_ranges"

    private const val PAGE_SEPARATOR = "§"

    @Volatile
    private var cachedPrefs: SharedPreferences? = null

    private fun prefs(context: Context): SharedPreferences {
        return cachedPrefs ?: synchronized(this) {
            cachedPrefs ?: context.applicationContext
                .getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
                .also { cachedPrefs = it }
        }
    }

    fun load(context: Context): BridgeConfig {
        val prefs = prefs(context)

        val pagesRaw = prefs.getString(KEY_PAGES, null)
        val pages = if (pagesRaw != null) {
            pagesRaw.split(PAGE_SEPARATOR).mapNotNull { DashPage.deserialize(it) }
        } else {
            migrateOldWidgets(prefs)
        }

        return BridgeConfig(
            connectionMode = ConnectionMode.fromKey(prefs.getString(KEY_CONNECTION_MODE, ConnectionMode.BLUETOOTH.key)),
            autoReconnect = true,
            vehicleProfile = VehicleProfile.fromKey(prefs.getString(KEY_VEHICLE_PROFILE, null)) ?: VehicleProfile.DEFAULT,
            pages = pages,
            btMacAddress = prefs.getString(KEY_BT_MAC, "") ?: "",
            setupComplete = prefs.getBoolean(KEY_SETUP_COMPLETE, false)
        )
    }

    fun save(context: Context, config: BridgeConfig) {
        val prefs = prefs(context)
        prefs.edit()
            .putBoolean(KEY_SETUP_COMPLETE, config.setupComplete)
            .putString(KEY_CONNECTION_MODE, config.connectionMode.key)
            .putBoolean(KEY_AUTO_RECONNECT, config.autoReconnect)
            .putString(KEY_VEHICLE_PROFILE, config.vehicleProfile.key)
            .putString(KEY_PAGES, config.pages.joinToString(PAGE_SEPARATOR) { it.serialize() })
            .putString(KEY_BT_MAC, config.btMacAddress.trim())
            .apply()
    }

    fun updateAutoReconnect(context: Context, enabled: Boolean) {
        prefs(context).edit().putBoolean(KEY_AUTO_RECONNECT, enabled).apply()
    }

    /** Returns custom DPI (default 300 for smartwatch). */
    fun getCustomDpi(context: Context): Int =
        prefs(context).getInt(KEY_CUSTOM_DPI, 300)

    fun setCustomDpi(context: Context, dpi: Int) {
        prefs(context).edit().putInt(KEY_CUSTOM_DPI, dpi).apply()
    }

    fun getScreenChargeControl(context: Context): Boolean =
        prefs(context).getBoolean(KEY_SCREEN_CHARGE_CONTROL, false)

    fun setScreenChargeControl(context: Context, enabled: Boolean) {
        prefs(context).edit().putBoolean(KEY_SCREEN_CHARGE_CONTROL, enabled).apply()
    }

    /** Wraps a Context with overridden density if a custom DPI is set. */
    fun applyCustomDpi(base: Context): Context {
        val dpi = getCustomDpi(base)
        if (dpi <= 0) return base
        val config = android.content.res.Configuration(base.resources.configuration)
        config.densityDpi = dpi
        return base.createConfigurationContext(config)
    }

    fun addPage(context: Context, page: DashPage, atIndex: Int = -1) {
        val config = load(context)
        val newPages = config.pages.toMutableList()
        if (atIndex in 0..newPages.size) newPages.add(atIndex, page) else newPages.add(page)
        save(context, config.copy(pages = newPages))
    }

    fun removePage(context: Context, pageIndex: Int) {
        val config = load(context)
        if (pageIndex !in config.pages.indices) return
        val newPages = config.pages.toMutableList()
        newPages.removeAt(pageIndex)
        save(context, config.copy(pages = newPages))
    }

    fun updatePageLayout(context: Context, pageIndex: Int, cols: Int, rows: Int) {
        val config = load(context)
        if (pageIndex !in config.pages.indices) return
        val newPages = config.pages.toMutableList()
        newPages[pageIndex] = newPages[pageIndex].withLayout(cols, rows)
        save(context, config.copy(pages = newPages))
    }

    fun setCell(context: Context, pageIndex: Int, cellIndex: Int, cellConfig: CellConfig?) {
        val config = load(context)
        if (pageIndex !in config.pages.indices) return
        val newPages = config.pages.toMutableList()
        newPages[pageIndex] = newPages[pageIndex].withCell(cellIndex, cellConfig)
        save(context, config.copy(pages = newPages))
    }

    fun getHiddenApps(context: Context): Set<String> {
        val prefs = prefs(context)
        return prefs.getStringSet(KEY_HIDDEN_APPS, emptySet()) ?: emptySet()
    }

    fun toggleHiddenApp(context: Context, packageName: String): Boolean {
        val prefs = prefs(context)
        val hidden = (prefs.getStringSet(KEY_HIDDEN_APPS, emptySet()) ?: emptySet()).toMutableSet()
        val isNowHidden = if (packageName in hidden) {
            hidden.remove(packageName)
            false
        } else {
            hidden.add(packageName)
            true
        }
        prefs.edit().putStringSet(KEY_HIDDEN_APPS, hidden).apply()
        return isNowHidden
    }

    fun getCustomLabel(context: Context, packageName: String): String? {
        val prefs = prefs(context)
        val raw = prefs.getString(KEY_APP_LABELS, null) ?: return null
        return raw.split("\n").firstOrNull { it.startsWith("$packageName\t") }
            ?.substringAfter("\t")
    }

    fun setCustomLabel(context: Context, packageName: String, label: String?) {
        val prefs = prefs(context)
        val raw = prefs.getString(KEY_APP_LABELS, null) ?: ""
        val entries = raw.split("\n")
            .filter { it.isNotBlank() && !it.startsWith("$packageName\t") }
            .toMutableList()
        if (label != null) entries.add("$packageName\t$label")
        prefs.edit().putString(KEY_APP_LABELS, entries.joinToString("\n")).apply()
    }

    fun getCustomIcon(context: Context, packageName: String): String? {
        val prefs = prefs(context)
        val raw = prefs.getString(KEY_APP_ICONS, null) ?: return null
        return raw.split("\n").firstOrNull { it.startsWith("$packageName\t") }
            ?.substringAfter("\t")
    }

    fun setCustomIcon(context: Context, packageName: String, iconRef: String?) {
        val prefs = prefs(context)
        val raw = prefs.getString(KEY_APP_ICONS, null) ?: ""
        val entries = raw.split("\n")
            .filter { it.isNotBlank() && !it.startsWith("$packageName\t") }
            .toMutableList()
        if (iconRef != null) entries.add("$packageName\t$iconRef")
        prefs.edit().putString(KEY_APP_ICONS, entries.joinToString("\n")).apply()
    }

    /** Wallpaper: "color:#AARRGGBB" or "image:/path/to/file" or null for default black */
    fun getWallpaper(context: Context): String? =
        prefs(context).getString(KEY_WALLPAPER, null)

    fun setWallpaper(context: Context, value: String?) {
        val prefs = prefs(context)
        if (value == null) prefs.edit().remove(KEY_WALLPAPER).apply()
        else prefs.edit().putString(KEY_WALLPAPER, value).apply()
    }

    private fun migrateOldWidgets(prefs: SharedPreferences): List<DashPage> {
        val widgetsRaw = prefs.getString(KEY_WIDGETS, null) ?: return emptyList()
        val widgets = widgetsRaw.split(',').mapNotNull { WidgetType.fromKey(it.trim()) }
        if (widgets.isEmpty()) return emptyList()

        val styles = prefs.getString(KEY_WIDGET_STYLES, null)
            ?.split(';')?.mapNotNull { entry ->
                val parts = entry.split(':')
                if (parts.size != 2) return@mapNotNull null
                val w = WidgetType.fromKey(parts[0].trim()) ?: return@mapNotNull null
                val faceId = FaceStore.STYLE_MIGRATION[parts[1].trim()] ?: return@mapNotNull null
                w to faceId
            }?.toMap().orEmpty()

        val units = prefs.getString(KEY_WIDGET_UNITS, null)
            ?.split(';')?.mapNotNull { entry ->
                val parts = entry.split(':')
                if (parts.size != 2) return@mapNotNull null
                val w = WidgetType.fromKey(parts[0].trim()) ?: return@mapNotNull null
                w to parts[1].trim()
            }?.toMap().orEmpty()

        val ranges = prefs.getString(KEY_WIDGET_RANGES, null)
            ?.split(';')?.mapNotNull { entry ->
                val sep = entry.indexOf(':')
                if (sep < 0) return@mapNotNull null
                val w = WidgetType.fromKey(entry.substring(0, sep).trim()) ?: return@mapNotNull null
                val r = GaugeRange.deserialize(entry.substring(sep + 1)) ?: return@mapNotNull null
                w to r
            }?.toMap().orEmpty()

        return widgets.map { widget ->
            DashPage(
                columns = 1, rows = 1,
                cells = listOf(
                    CellConfig(
                        widgetType = widget,
                        faceId = styles[widget] ?: FaceStore.defaultFaceFor(widget),
                        unitKey = units[widget] ?: "",
                        range = ranges[widget]
                    )
                )
            )
        }
    }
}
