use super::*;

/// Settings that control how certain types are rendered by `dear-imgui-reflect`.
///
/// This mirrors some of the concepts from ImReflect's `ImSettings` type, but is
/// intentionally smaller and focused on common container behaviors.
#[derive(Clone, Debug, Default)]
pub struct ReflectSettings {
    vec: VecSettings,
    bools: BoolSettings,
    arrays: ArraySettings,
    maps: MapSettings,
    tuples: TupleSettings,
    numerics_i32: NumericTypeSettings,
    numerics_f32: NumericTypeSettings,
    numerics_u32: NumericTypeSettings,
    numerics_f64: NumericTypeSettings,
    member_overrides: HashMap<TypeId, HashMap<String, MemberSettings>>,
}

impl ReflectSettings {
    /// Creates a new settings object with reasonable defaults.
    pub fn new() -> Self {
        Self::default()
    }

    /// Settings that apply to all `Vec<T>` containers rendered via reflection.
    pub fn vec(&self) -> &VecSettings {
        &self.vec
    }

    /// Mutable access to settings that apply to all `Vec<T>` containers.
    pub fn vec_mut(&mut self) -> &mut VecSettings {
        &mut self.vec
    }

    /// Settings that apply to all `bool` fields rendered via reflection.
    pub fn bools(&self) -> &BoolSettings {
        &self.bools
    }

    /// Mutable access to settings that apply to all `bool` fields.
    pub fn bools_mut(&mut self) -> &mut BoolSettings {
        &mut self.bools
    }

    /// Settings that apply to fixed-size arrays rendered via reflection.
    pub fn arrays(&self) -> &ArraySettings {
        &self.arrays
    }

    /// Mutable access to settings that apply to fixed-size arrays.
    pub fn arrays_mut(&mut self) -> &mut ArraySettings {
        &mut self.arrays
    }

    /// Settings that apply to string-keyed maps rendered via reflection.
    pub fn maps(&self) -> &MapSettings {
        &self.maps
    }

    /// Mutable access to settings that apply to string-keyed maps.
    pub fn maps_mut(&mut self) -> &mut MapSettings {
        &mut self.maps
    }

    /// Settings that apply to tuple-like values rendered via reflection.
    pub fn tuples(&self) -> &TupleSettings {
        &self.tuples
    }

    /// Mutable access to settings that apply to tuple-like values.
    pub fn tuples_mut(&mut self) -> &mut TupleSettings {
        &mut self.tuples
    }

    /// Type-level numeric settings for `i32` values rendered via reflection.
    pub fn numerics_i32(&self) -> &NumericTypeSettings {
        &self.numerics_i32
    }

    /// Mutable access to type-level numeric settings for `i32` values.
    pub fn numerics_i32_mut(&mut self) -> &mut NumericTypeSettings {
        &mut self.numerics_i32
    }

    /// Type-level numeric settings for `f32` values rendered via reflection.
    pub fn numerics_f32(&self) -> &NumericTypeSettings {
        &self.numerics_f32
    }

    /// Mutable access to type-level numeric settings for `f32` values.
    pub fn numerics_f32_mut(&mut self) -> &mut NumericTypeSettings {
        &mut self.numerics_f32
    }

    /// Type-level numeric settings for `u32` values rendered via reflection.
    pub fn numerics_u32(&self) -> &NumericTypeSettings {
        &self.numerics_u32
    }

    /// Mutable access to type-level numeric settings for `u32` values.
    pub fn numerics_u32_mut(&mut self) -> &mut NumericTypeSettings {
        &mut self.numerics_u32
    }

    /// Type-level numeric settings for `f64` values rendered via reflection.
    pub fn numerics_f64(&self) -> &NumericTypeSettings {
        &self.numerics_f64
    }

    /// Mutable access to type-level numeric settings for `f64` values.
    pub fn numerics_f64_mut(&mut self) -> &mut NumericTypeSettings {
        &mut self.numerics_f64
    }

    /// Returns member-level settings for a given type and field name, if any.
    ///
    /// This provides an ImSettings-style per-member override analogous to
    /// `push_member<&T::field>()` in ImReflect.
    ///
    /// Member keys follow the paths generated by the derive macro:
    ///
    /// - Named-field structs: `"field_name"`
    /// - Tuple structs: `"0"`, `"1"`, ...
    /// - Enums with payloads: `"Variant.field_name"` or `"Variant.0"`, `"Variant.1"`, ...
    /// - Tuple element overrides: `"tuple_field[0]"`, `"tuple_field[1]"`, ...
    pub fn member<T: 'static>(&self, field: &str) -> Option<&MemberSettings> {
        self.member_overrides
            .get(&TypeId::of::<T>())
            .and_then(|map| map.get(field))
    }

    /// Returns a mutable handle to member-level settings for a given type and
    /// field name, creating a default entry if it does not yet exist.
    ///
    /// See [`ReflectSettings::member`] for the expected member-key formats.
    pub fn for_member<T: 'static>(&mut self, field: &str) -> &mut MemberSettings {
        self.member_overrides
            .entry(TypeId::of::<T>())
            .or_default()
            .entry(field.to_owned())
            .or_default()
    }
}

static GLOBAL_SETTINGS: OnceLock<Mutex<ReflectSettings>> = OnceLock::new();

fn settings_mutex() -> &'static Mutex<ReflectSettings> {
    GLOBAL_SETTINGS.get_or_init(|| Mutex::new(ReflectSettings::default()))
}

/// Runs `f` with a shared reference to the current global `ReflectSettings`.
///
/// This helper avoids cloning the settings object on read-only access paths
/// (such as container `ImGuiValue` implementations) while preserving the
/// existing `current_settings()` API for callers that need an owned copy.
pub(crate) fn with_settings_read<F, R>(f: F) -> R
where
    F: FnOnce(&ReflectSettings) -> R,
{
    let guard = settings_mutex()
        .lock()
        .unwrap_or_else(|err| err.into_inner());
    f(&guard)
}

/// Returns a clone of the current global `ReflectSettings`.
///
/// This is used internally by container editors to honor type-level defaults.
pub fn current_settings() -> ReflectSettings {
    settings_mutex()
        .lock()
        .unwrap_or_else(|err| err.into_inner())
        .clone()
}

/// Mutates the global `ReflectSettings` in place.
///
/// This provides an `ImSettings`-style entry point to configure type-level
/// defaults for container editing behavior.
pub fn with_settings<F, R>(f: F) -> R
where
    F: FnOnce(&mut ReflectSettings) -> R,
{
    let mut guard = settings_mutex()
        .lock()
        .unwrap_or_else(|err| err.into_inner());
    f(&mut guard)
}

/// Executes a closure with a temporary modification of the global
/// [`ReflectSettings`], restoring the previous settings afterwards.
///
/// This provides a lightweight, ImSettings-style "scope" mechanism for
/// experiments or per-panel overrides without introducing a full push/pop
/// stack API. A typical usage pattern is:
///
/// ```no_run
/// # use dear_imgui_reflect as reflect;
/// # use reflect::ImGuiReflectExt;
/// #
/// # #[derive(reflect::ImGuiReflect, Default)]
/// # struct MyType {
/// #     items: Vec<i32>,
/// # }
/// #
/// # fn ui_frame(ui: &reflect::imgui::Ui, value: &mut MyType) {
/// reflect::with_settings_scope(|| {
///     reflect::with_settings(|s| {
///         s.for_member::<MyType>("items").vec_reorder_only();
///     });
///     ui.input_reflect("Debug Items", value);
/// });
/// # }
/// ```
///
/// Inside the scope, `with_settings` can be used freely; once the closure
/// returns, the previous `ReflectSettings` are restored.
pub fn with_settings_scope<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    struct RestoreGuard {
        saved: Option<ReflectSettings>,
    }

    impl Drop for RestoreGuard {
        fn drop(&mut self) {
            // Avoid panicking in Drop: if the mutex is poisoned, restoring the
            // settings is best-effort only.
            let Some(saved) = self.saved.take() else {
                return;
            };
            if let Ok(mut guard) = settings_mutex().lock() {
                *guard = saved;
            }
        }
    }

    // Snapshot current settings.
    let saved = current_settings();
    let _guard = RestoreGuard { saved: Some(saved) };
    // Run user code, allowing it to call `with_settings` as needed.
    f()
}
