//! Application state management traits and types
//!
//! Implements R-STATE-SEPARATION: Complete separation of state and rendering logic.
//! All application state is held in structs implementing the traits in this module,
//! while rendering is handled by separate functions that only read state.

use std::fmt;

/// Base trait for application state
///
/// Implementers must:
/// - Keep all application logic separate from rendering
/// - Only mutate state through methods, never expose direct field access for mutation
/// - Track dirty flag for efficient rendering
///
/// # Example
///
/// ```ignore
/// #[derive(Clone, Default, Debug)]
/// pub struct MyAppState {
///     pub counter: u32,
///     pub items: Vec<String>,
///     pub should_quit: bool,
///     dirty: bool,
/// }
///
/// impl MyAppState {
///     pub fn increment(&mut self) {
///         self.counter = self.counter.saturating_add(1);
///         self.dirty = true;
///     }
///
///     pub fn is_dirty(&self) -> bool {
///         self.dirty
///     }
///
///     pub fn reset_dirty(&mut self) {
///         self.dirty = false;
///     }
/// }
/// ```
pub trait AppState: Send + Sync {
    /// Check if state has changed since last render
    fn is_dirty(&self) -> bool;

    /// Reset dirty flag after rendering
    fn reset_dirty(&mut self);

    /// Check if application should quit
    fn should_quit(&self) -> bool;
}

/// Screen trait for different views/screens in the application
///
/// Each screen represents a distinct view that can be rendered.
/// Screens should only read application state, never modify it.
///
/// # Example
///
/// ```ignore
/// pub struct ListScreen;
///
/// impl Screen for ListScreen {
///     fn name(&self) -> &str {
///         "list"
///     }
///
///     fn render(&self, state: &dyn AppState, frame: &mut Frame, area: Rect) {
///         // Rendering logic that only reads state
///     }
/// }
/// ```
pub trait Screen: Send + Sync {
    /// Screen identifier
    fn name(&self) -> &str;

    /// Render this screen given current state
    ///
    /// # Arguments
    ///
    /// * `state` - Current application state (read-only)
    /// * `frame` - `RataTui` frame for drawing
    /// * `area` - Available drawing area
    fn render(&self, state: &dyn AppState, frame: &mut ratatui::Frame, area: ratatui::layout::Rect);
}

/// Navigation state for screen transitions
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub enum NavigationAction {
    /// Show a specific screen by name
    ShowScreen(String),
    /// Pop to previous screen
    Pop,
    /// Push a new screen onto stack
    Push(String),
    /// Clear stack and show screen
    Reset(String),
    /// No change
    #[default]
    None,
}

impl fmt::Display for NavigationAction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::ShowScreen(s) => write!(f, "ShowScreen({s})"),
            Self::Pop => write!(f, "Pop"),
            Self::Push(s) => write!(f, "Push({s})"),
            Self::Reset(s) => write!(f, "Reset({s})"),
            Self::None => write!(f, "None"),
        }
    }
}

/// Focus state for form and list widgets
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Focus {
    /// Nothing is focused
    #[default]
    None,
    /// Sidebar/menu is focused
    Sidebar,
    /// Main content area is focused
    Content,
    /// A modal/dialog is focused
    Modal,
}

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

    #[test]
    fn test_navigation_action_default() {
        assert_eq!(NavigationAction::default(), NavigationAction::None);
    }

    #[test]
    fn test_navigation_action_display() {
        let actions = [
            (
                NavigationAction::ShowScreen("test".to_string()),
                "ShowScreen(test)",
            ),
            (NavigationAction::Pop, "Pop"),
            (NavigationAction::Push("test".to_string()), "Push(test)"),
            (NavigationAction::Reset("test".to_string()), "Reset(test)"),
            (NavigationAction::None, "None"),
        ];

        for (action, expected) in &actions {
            assert_eq!(action.to_string(), *expected);
        }
    }

    #[test]
    fn test_focus_default() {
        assert_eq!(Focus::default(), Focus::None);
    }
}
