// Copyright (C) 2026 Jason M. Schwefel. All Rights Reserved.
package com.coldboreballisticsllc.scopedope.data.prefs

import android.content.Context
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.doublePreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import com.coldboreballisticsllc.scopedope.data.session.RecentSession
import javax.inject.Inject
import javax.inject.Singleton

private val Context.appPrefsDataStore by preferencesDataStore(name = "app_prefs")

@Singleton
class AppPreferences @Inject constructor(
    @ApplicationContext private val context: Context,
) {
    private val KEY_ONBOARDING             = booleanPreferencesKey("onboarding_complete")
    private val KEY_USE_METRIC             = booleanPreferencesKey("use_metric")
    private val KEY_PRINT_FULL_CIRCLE      = booleanPreferencesKey("print_full_circle")
    private val KEY_SHOW_GRIDLINES         = booleanPreferencesKey("show_gridlines")
    private val KEY_ELEVATION_SYMBOL       = stringPreferencesKey("elevation_symbol")
    private val KEY_COLUMN_FILL_INTERLEAVED = booleanPreferencesKey("column_fill_interleaved")
    private val KEY_LANDSCAPE_COL1_REVERSED = booleanPreferencesKey("landscape_col1_reversed")

    val onboardingComplete: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_ONBOARDING] ?: false }

    suspend fun setOnboardingComplete() {
        context.appPrefsDataStore.edit { it[KEY_ONBOARDING] = true }
    }

    suspend fun resetOnboarding() {
        context.appPrefsDataStore.edit { it[KEY_ONBOARDING] = false }
    }

    val useMetric: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_USE_METRIC] ?: false }

    suspend fun setUseMetric(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_USE_METRIC] = value }
    }

    val printFullCircle: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_PRINT_FULL_CIRCLE] ?: false }

    suspend fun setPrintFullCircle(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_PRINT_FULL_CIRCLE] = value }
    }

    val showGridlines: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_SHOW_GRIDLINES] ?: true }

    suspend fun setShowGridlines(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_SHOW_GRIDLINES] = value }
    }

    val elevationSymbol: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_ELEVATION_SYMBOL] ?: "PLUS_MINUS" }

    suspend fun setElevationSymbol(value: String) {
        context.appPrefsDataStore.edit { it[KEY_ELEVATION_SYMBOL] = value }
    }

    val columnFillInterleaved: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_COLUMN_FILL_INTERLEAVED] ?: true }

    suspend fun setColumnFillInterleaved(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_COLUMN_FILL_INTERLEAVED] = value }
    }

    val landscapeCol1Reversed: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_LANDSCAPE_COL1_REVERSED] ?: true }

    suspend fun setLandscapeCol1Reversed(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_LANDSCAPE_COL1_REVERSED] = value }
    }

    private val KEY_USE_SEMANTIC_COLORS = booleanPreferencesKey("use_semantic_colors")

    val useSemanticColors: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_USE_SEMANTIC_COLORS] ?: false }

    suspend fun setUseSemanticColors(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_USE_SEMANTIC_COLORS] = value }
    }

    private val KEY_LABEL_FORMAT_ID = stringPreferencesKey("label_format_id")

    val labelFormatId: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_LABEL_FORMAT_ID] ?: "circle_50mm" }

    suspend fun setLabelFormatId(id: String) {
        context.appPrefsDataStore.edit { it[KEY_LABEL_FORMAT_ID] = id }
    }

    private val KEY_CLICK_VALUE          = stringPreferencesKey("click_value")
    private val KEY_SAVED_MOA_CLICK      = stringPreferencesKey("saved_moa_click")
    private val KEY_SAVED_MRAD_CLICK     = stringPreferencesKey("saved_mrad_click")

    val clickValue: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_CLICK_VALUE] ?: "0.1_mrad" }

    suspend fun setClickValue(value: String) {
        context.appPrefsDataStore.edit { it[KEY_CLICK_VALUE] = value }
    }

    suspend fun getSavedMoaClick(): String =
        context.appPrefsDataStore.data.first()[KEY_SAVED_MOA_CLICK] ?: "0.25_moa"

    suspend fun setSavedMoaClick(value: String) {
        context.appPrefsDataStore.edit { it[KEY_SAVED_MOA_CLICK] = value }
    }

    suspend fun getSavedMradClick(): String =
        context.appPrefsDataStore.data.first()[KEY_SAVED_MRAD_CLICK] ?: "0.1_mrad"

    suspend fun setSavedMradClick(value: String) {
        context.appPrefsDataStore.edit { it[KEY_SAVED_MRAD_CLICK] = value }
    }

    private val KEY_USE_CORIOLIS         = booleanPreferencesKey("use_coriolis")
    private val KEY_CORIOLIS_LATITUDE    = doublePreferencesKey("coriolis_latitude_deg")

    val useCoriolis: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_USE_CORIOLIS] ?: false }

    suspend fun setUseCoriolis(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_USE_CORIOLIS] = value }
    }

    val coriolisLatitudeDeg: Flow<Double> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_CORIOLIS_LATITUDE] ?: 0.0 }

    suspend fun setCoriolisLatitudeDeg(value: Double) {
        context.appPrefsDataStore.edit { it[KEY_CORIOLIS_LATITUDE] = value }
    }

    private val KEY_USE_REAL_DOPE = booleanPreferencesKey("use_real_dope")

    val useRealDope: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_USE_REAL_DOPE] ?: true }

    suspend fun setUseRealDope(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_USE_REAL_DOPE] = value }
    }

    private val KEY_LAST_REVIEW_VERSION_CODE = intPreferencesKey("last_review_version_code")

    val lastReviewVersionCode: Flow<Int> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_LAST_REVIEW_VERSION_CODE] ?: 0 }

    suspend fun setLastReviewVersionCode(versionCode: Int) {
        context.appPrefsDataStore.edit { it[KEY_LAST_REVIEW_VERSION_CODE] = versionCode }
    }

    // ── Crash reporting consent (opt-in, OFF by default — SPEC §16.1) ──────────
    // Default false: crash reporting transmits nothing until the user explicitly
    // consents. The prompt-shown flag gates the one-time first-run consent dialog.

    private val KEY_CRASH_REPORTING_ENABLED      = booleanPreferencesKey("crash_reporting_enabled")
    private val KEY_CRASH_REPORTING_PROMPT_SHOWN = booleanPreferencesKey("crash_reporting_prompt_shown")

    val crashReportingEnabled: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_CRASH_REPORTING_ENABLED] ?: false }

    suspend fun setCrashReportingEnabled(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_CRASH_REPORTING_ENABLED] = value }
    }

    val crashReportingPromptShown: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_CRASH_REPORTING_PROMPT_SHOWN] ?: false }

    suspend fun setCrashReportingPromptShown() {
        context.appPrefsDataStore.edit { it[KEY_CRASH_REPORTING_PROMPT_SHOWN] = true }
    }

    // ── Recent session history ────────────────────────────────────────────────

    private val KEY_RECENT_SESSIONS = stringPreferencesKey("recent_sessions")

    val recentSessions: Flow<List<RecentSession>> = context.appPrefsDataStore.data
        .map { prefs ->
            val raw = prefs[KEY_RECENT_SESSIONS] ?: return@map emptyList()
            runCatching { Json.decodeFromString<List<RecentSession>>(raw) }.getOrElse { emptyList() }
        }

    suspend fun addToRecentSessions(entry: RecentSession) {
        context.appPrefsDataStore.edit { prefs ->
            val raw = prefs[KEY_RECENT_SESSIONS]
            val list = raw?.let {
                runCatching { Json.decodeFromString<List<RecentSession>>(it) }.getOrElse { emptyList() }
            } ?: emptyList()
            // Evict existing entry for same file path, prepend new, cap at 20
            val updated = (listOf(entry) + list.filter { it.filePath != entry.filePath }).take(20)
            prefs[KEY_RECENT_SESSIONS] = Json.encodeToString(updated)
        }
    }

    suspend fun removeFromRecentSessions(filePath: String) {
        context.appPrefsDataStore.edit { prefs ->
            val raw = prefs[KEY_RECENT_SESSIONS] ?: return@edit
            val list = runCatching { Json.decodeFromString<List<RecentSession>>(raw) }.getOrElse { emptyList() }
            prefs[KEY_RECENT_SESSIONS] = Json.encodeToString(list.filter { it.filePath != filePath })
        }
    }

    // ── Phone compass for wind direction ──────────────────────────────────────

    private val KEY_USE_PHONE_COMPASS_WIND = booleanPreferencesKey("use_phone_compass_wind")

    val usePhoneCompassWind: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_USE_PHONE_COMPASS_WIND] ?: true }

    suspend fun setUsePhoneCompassWind(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_USE_PHONE_COMPASS_WIND] = value }
    }

    // ── Active weather sensor ─────────────────────────────────────────────────

    private val KEY_SENSOR_DRIVER_ID = stringPreferencesKey("sensor_driver_id")

    val sensorDriverId: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_SENSOR_DRIVER_ID] ?: "weatherflow_tactical" }

    suspend fun setSensorDriverId(id: String) {
        context.appPrefsDataStore.edit { it[KEY_SENSOR_DRIVER_ID] = id }
    }

    // ── Per-field condition units ─────────────────────────────────────────────

    private val KEY_ALTITUDE_UNIT   = stringPreferencesKey("altitude_unit")
    private val KEY_TEMP_UNIT       = stringPreferencesKey("temp_unit")
    private val KEY_PRESSURE_UNIT   = stringPreferencesKey("pressure_unit")
    private val KEY_WIND_SPEED_UNIT = stringPreferencesKey("wind_speed_unit")

    val altitudeUnit: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_ALTITUDE_UNIT] ?: "FT" }

    suspend fun setAltitudeUnit(value: String) {
        context.appPrefsDataStore.edit { it[KEY_ALTITUDE_UNIT] = value }
    }

    val tempUnit: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_TEMP_UNIT] ?: "F" }

    suspend fun setTempUnit(value: String) {
        context.appPrefsDataStore.edit { it[KEY_TEMP_UNIT] = value }
    }

    val pressureUnit: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_PRESSURE_UNIT] ?: "INHG" }

    suspend fun setPressureUnit(value: String) {
        context.appPrefsDataStore.edit { it[KEY_PRESSURE_UNIT] = value }
    }

    val windSpeedUnit: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_WIND_SPEED_UNIT] ?: "MPH" }

    suspend fun setWindSpeedUnit(value: String) {
        context.appPrefsDataStore.edit { it[KEY_WIND_SPEED_UNIT] = value }
    }

    // ── Printer friendly names ────────────────────────────────────────────────

    suspend fun getPrinterName(mac: String): String? =
        printerRegistry.first().find { it.mac == mac }?.friendlyName

    // ── Printer registry ──────────────────────────────────────────────────────────

    private val KEY_PRINTER_REGISTRY = stringPreferencesKey("printer_registry")

    val printerRegistry: Flow<List<StoredPrinterEntry>> = context.appPrefsDataStore.data
        .map { prefs ->
            val raw = prefs[KEY_PRINTER_REGISTRY] ?: return@map emptyList()
            runCatching { Json.decodeFromString<List<StoredPrinterEntry>>(raw) }.getOrElse { emptyList() }
        }

    suspend fun upsertStoredPrinter(entry: StoredPrinterEntry) {
        context.appPrefsDataStore.edit { mp ->
            val raw = mp[KEY_PRINTER_REGISTRY]
            val list = raw?.let {
                runCatching { Json.decodeFromString<List<StoredPrinterEntry>>(it) }.getOrElse { emptyList() }
            } ?: emptyList()
            mp[KEY_PRINTER_REGISTRY] = Json.encodeToString(list.filter { it.mac != entry.mac } + entry)
        }
    }

    suspend fun removeStoredPrinter(mac: String) {
        context.appPrefsDataStore.edit { mp ->
            val raw = mp[KEY_PRINTER_REGISTRY] ?: return@edit
            val list = runCatching { Json.decodeFromString<List<StoredPrinterEntry>>(raw) }.getOrElse { emptyList() }
            mp[KEY_PRINTER_REGISTRY] = Json.encodeToString(list.filter { it.mac != mac })
        }
    }

    // ── Printer shared mode ───────────────────────────────────────────────────────

    private val KEY_PRINTER_SHARED_MODE = booleanPreferencesKey("printer_shared_mode")

    val printerSharedMode: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_PRINTER_SHARED_MODE] ?: false }

    suspend fun setPrinterSharedMode(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_PRINTER_SHARED_MODE] = value }
    }

    // ── Sensor friendly name (per driver ID) ──────────────────────────────────────

    suspend fun getSensorFriendlyName(driverId: String): String? {
        val key = stringPreferencesKey("sensor_friendly_name_$driverId")
        return context.appPrefsDataStore.data.first()[key]?.takeIf { it.isNotBlank() }
    }

    suspend fun setSensorFriendlyName(driverId: String, name: String) {
        val key = stringPreferencesKey("sensor_friendly_name_$driverId")
        context.appPrefsDataStore.edit { mp ->
            if (name.isBlank()) mp.remove(key) else mp[key] = name
        }
    }

    // ── Sensor shared mode ────────────────────────────────────────────────────────

    private val KEY_SENSOR_SHARED_MODE = booleanPreferencesKey("sensor_shared_mode")

    val sensorSharedMode: Flow<Boolean> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_SENSOR_SHARED_MODE] ?: false }

    suspend fun setSensorSharedMode(value: Boolean) {
        context.appPrefsDataStore.edit { it[KEY_SENSOR_SHARED_MODE] = value }
    }

    private val KEY_SENSOR_SHARED_SUBMODE = stringPreferencesKey("sensor_shared_submode")

    val sensorSharedSubMode: Flow<String> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_SENSOR_SHARED_SUBMODE] ?: "READ_ONCE" }

    suspend fun setSensorSharedSubMode(value: String) {
        context.appPrefsDataStore.edit { it[KEY_SENSOR_SHARED_SUBMODE] = value }
    }

    private val KEY_SENSOR_SHARED_WINDOW_SECONDS = intPreferencesKey("sensor_shared_window_seconds")

    val sensorSharedWindowSeconds: Flow<Int> = context.appPrefsDataStore.data
        .map { prefs -> prefs[KEY_SENSOR_SHARED_WINDOW_SECONDS] ?: 30 }

    suspend fun setSensorSharedWindowSeconds(value: Int) {
        context.appPrefsDataStore.edit { it[KEY_SENSOR_SHARED_WINDOW_SECONDS] = value }
    }
}
