//! Input mode for the command field.
//!
//! See ADR-0003 for the design. The two modes determine how the
//! input field interprets a submitted line. The `:` one-shot
//! escape from simple to advanced is handled at submission time
//! in `app::App::submit`, not as additional state here.

use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Mode {
    /// The teaching DSL only — the app's startup mode (ADR-0003)
    /// and the walker's default view: SQL-only grammar is gated
    /// out (ADR-0030 §2).
    #[default]
    Simple,
    Advanced,
}

impl Mode {
    pub const fn label(self) -> &'static str {
        match self {
            Self::Simple => "SIMPLE",
            Self::Advanced => "ADVANCED",
        }
    }

    /// The lowercase keyword form used wherever the mode is
    /// written or read as plain text — the `--mode` CLI flag, the
    /// `mode <value>` command, and the `project.yaml` `mode:`
    /// field (ADR-0015 mode-restore amendment, issue #14). Kept
    /// distinct from `label()` (the uppercase UI banner form) so
    /// the on-disk / CLI vocabulary is stable and case-consistent.
    pub const fn keyword(self) -> &'static str {
        match self {
            Self::Simple => "simple",
            Self::Advanced => "advanced",
        }
    }

    /// Parse a mode keyword, case-insensitively. `None` for any
    /// other string. The single source of truth for "simple" /
    /// "advanced" text recognition across the CLI flag, the
    /// `mode` command, and the `project.yaml` reader.
    #[must_use]
    pub fn from_keyword(s: &str) -> Option<Self> {
        match s.trim().to_ascii_lowercase().as_str() {
            "simple" => Some(Self::Simple),
            "advanced" => Some(Self::Advanced),
            _ => None,
        }
    }

    /// Resolve the startup input mode (ADR-0015 mode-restore
    /// amendment, issue #14). Precedence: the `--mode` CLI override
    /// (`flag`) wins; otherwise the project's stored mode
    /// (`stored`); otherwise the default (`Simple`). `stored` is
    /// `None` for a project with no recorded preference.
    #[must_use]
    pub fn resolve_startup(flag: Option<Self>, stored: Option<Self>) -> Self {
        flag.or(stored).unwrap_or_default()
    }
}

impl fmt::Display for Mode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.label())
    }
}

#[cfg(test)]
mod tests {
    use super::Mode;

    #[test]
    fn keyword_round_trips_through_from_keyword() {
        for mode in [Mode::Simple, Mode::Advanced] {
            assert_eq!(Mode::from_keyword(mode.keyword()), Some(mode));
        }
    }

    #[test]
    fn from_keyword_is_case_insensitive_and_trims() {
        assert_eq!(Mode::from_keyword("  Advanced "), Some(Mode::Advanced));
        assert_eq!(Mode::from_keyword("SIMPLE"), Some(Mode::Simple));
    }

    #[test]
    fn from_keyword_rejects_unknown() {
        assert_eq!(Mode::from_keyword("expert"), None);
        assert_eq!(Mode::from_keyword(""), None);
    }

    #[test]
    fn keyword_is_lowercase_distinct_from_label() {
        // `keyword()` is the on-disk/CLI form; `label()` is the
        // uppercase UI banner. They must not be conflated.
        assert_eq!(Mode::Advanced.keyword(), "advanced");
        assert_eq!(Mode::Advanced.label(), "ADVANCED");
    }

    #[test]
    fn resolve_startup_applies_flag_then_stored_then_default() {
        // Flag wins over everything.
        assert_eq!(
            Mode::resolve_startup(Some(Mode::Advanced), Some(Mode::Simple)),
            Mode::Advanced,
        );
        assert_eq!(
            Mode::resolve_startup(Some(Mode::Simple), Some(Mode::Advanced)),
            Mode::Simple,
        );
        // No flag → stored mode.
        assert_eq!(
            Mode::resolve_startup(None, Some(Mode::Advanced)),
            Mode::Advanced,
        );
        // No flag, no stored preference → default (Simple).
        assert_eq!(Mode::resolve_startup(None, None), Mode::Simple);
    }
}
