package com.onthelevel.core.settings

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.doublePreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.onthelevel.core.sensors.EdgeCalibration
import com.onthelevel.core.sensors.SurfaceCalibration
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

/**
 * DataStore-backed preferences and calibration persistence (BRIEF.md core/settings).
 * Calibration is stored PER MODE and applied only to that mode's readings.
 */
class SettingsRepository(context: Context) {

    private val store = context.applicationContext.dataStore

    val surfaceCalibration: Flow<SurfaceCalibration> = store.data.map { prefs ->
        SurfaceCalibration(
            pitchBiasDegrees = prefs[Keys.SURFACE_PITCH_BIAS] ?: 0.0,
            rollBiasDegrees = prefs[Keys.SURFACE_ROLL_BIAS] ?: 0.0,
        )
    }

    val edgeCalibration: Flow<EdgeCalibration> = store.data.map { prefs ->
        EdgeCalibration(
            levelBiasDegrees = prefs[Keys.EDGE_LEVEL_BIAS] ?: 0.0,
            calibratedOnPositiveXEdge = prefs[Keys.EDGE_CAL_POSITIVE_X] ?: true,
        )
    }

    val hapticsEnabled: Flow<Boolean> = store.data.map { it[Keys.HAPTICS_ENABLED] ?: true }

    /** Off / Tones / Voice, migrating from the legacy audio-cue boolean if unset. */
    val audioAssistMode: Flow<AudioAssistMode> = store.data.map { prefs ->
        when (prefs[Keys.AUDIO_ASSIST_MODE]) {
            AudioAssistMode.TONES.name -> AudioAssistMode.TONES
            AudioAssistMode.VOICE.name -> AudioAssistMode.VOICE
            AudioAssistMode.OFF.name -> AudioAssistMode.OFF
            else -> if (prefs[Keys.AUDIO_CUE_ENABLED] == true) AudioAssistMode.TONES else AudioAssistMode.OFF
        }
    }

    /** In-app reduced-motion preference; the system animator-scale signal is respected separately. */
    val reducedMotion: Flow<Boolean> = store.data.map { it[Keys.REDUCED_MOTION] ?: false }

    /** Kept in core so every future measurement tool formats slope consistently. */
    val measurementUnits: Flow<MeasurementUnits> = store.data.map { prefs ->
        when (prefs[Keys.MEASUREMENT_UNITS]) {
            MeasurementUnits.IMPERIAL.name -> MeasurementUnits.IMPERIAL
            else -> MeasurementUnits.METRIC
        }
    }

    val calibrationMode: Flow<CalibrationMode> = store.data.map { prefs ->
        when (prefs[Keys.CALIBRATION_MODE]) {
            CalibrationMode.THOROUGH.name -> CalibrationMode.THOROUGH
            else -> CalibrationMode.SIMPLE
        }
    }

    suspend fun setSurfaceCalibration(calibration: SurfaceCalibration) {
        store.edit {
            it[Keys.SURFACE_PITCH_BIAS] = calibration.pitchBiasDegrees
            it[Keys.SURFACE_ROLL_BIAS] = calibration.rollBiasDegrees
        }
    }

    suspend fun clearSurfaceCalibration() = setSurfaceCalibration(SurfaceCalibration.NONE)

    suspend fun setEdgeCalibration(calibration: EdgeCalibration) {
        store.edit {
            it[Keys.EDGE_LEVEL_BIAS] = calibration.levelBiasDegrees
            it[Keys.EDGE_CAL_POSITIVE_X] = calibration.calibratedOnPositiveXEdge
        }
    }

    suspend fun setHapticsEnabled(enabled: Boolean) {
        store.edit { it[Keys.HAPTICS_ENABLED] = enabled }
    }

    suspend fun setAudioCueEnabled(enabled: Boolean) {
        store.edit { it[Keys.AUDIO_CUE_ENABLED] = enabled }
    }

    suspend fun setAudioAssistMode(mode: AudioAssistMode) {
        store.edit { it[Keys.AUDIO_ASSIST_MODE] = mode.name }
    }

    suspend fun setReducedMotion(enabled: Boolean) {
        store.edit { it[Keys.REDUCED_MOTION] = enabled }
    }

    suspend fun setMeasurementUnits(units: MeasurementUnits) {
        store.edit { it[Keys.MEASUREMENT_UNITS] = units.name }
    }

    suspend fun setCalibrationMode(mode: CalibrationMode) {
        store.edit { it[Keys.CALIBRATION_MODE] = mode.name }
    }

    // TODO(ruler): screen-ruler scale keyed by display identity/characteristics (BRIEF.md).

    private object Keys {
        val SURFACE_PITCH_BIAS = doublePreferencesKey("surface_pitch_bias_deg")
        val SURFACE_ROLL_BIAS = doublePreferencesKey("surface_roll_bias_deg")
        val EDGE_LEVEL_BIAS = doublePreferencesKey("edge_level_bias_deg")
        val EDGE_CAL_POSITIVE_X = booleanPreferencesKey("edge_cal_positive_x")
        val HAPTICS_ENABLED = booleanPreferencesKey("haptics_enabled")
        val AUDIO_CUE_ENABLED = booleanPreferencesKey("audio_cue_enabled")
        val AUDIO_ASSIST_MODE = stringPreferencesKey("audio_assist_mode")
        val REDUCED_MOTION = booleanPreferencesKey("reduced_motion")
        val MEASUREMENT_UNITS = stringPreferencesKey("measurement_units")
        val CALIBRATION_MODE = stringPreferencesKey("calibration_mode")
    }
}
