//! Who (people intelligence) screen state.
//!
//! Manages 5 query modes (Expert, Workload, Reviews, Active, Overlap),
//! input fields (path or username depending on mode), and result display.

use lore::core::who_types::WhoResult;

use crate::text_width::{next_char_boundary, prev_char_boundary};

// ---------------------------------------------------------------------------
// WhoMode
// ---------------------------------------------------------------------------

/// The 5 query modes for the who screen.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WhoMode {
    /// File-path expertise scores.
    #[default]
    Expert,
    /// Issue/MR assignment workload for a username.
    Workload,
    /// Review activity breakdown for a username.
    Reviews,
    /// Recent unresolved discussions (no input needed).
    Active,
    /// Shared file knowledge between contributors.
    Overlap,
}

impl WhoMode {
    /// Short label for mode tab rendering.
    #[must_use]
    pub fn label(self) -> &'static str {
        match self {
            Self::Expert => "Expert",
            Self::Workload => "Workload",
            Self::Reviews => "Reviews",
            Self::Active => "Active",
            Self::Overlap => "Overlap",
        }
    }

    /// Whether this mode requires a path input.
    #[must_use]
    pub fn needs_path(self) -> bool {
        matches!(self, Self::Expert | Self::Overlap)
    }

    /// Whether this mode requires a username input.
    #[must_use]
    pub fn needs_username(self) -> bool {
        matches!(self, Self::Workload | Self::Reviews)
    }

    /// Whether include_closed affects this mode's query.
    #[must_use]
    pub fn affected_by_include_closed(self) -> bool {
        matches!(self, Self::Workload | Self::Active)
    }

    /// Cycle to the next mode (wraps around).
    #[must_use]
    pub fn next(self) -> Self {
        match self {
            Self::Expert => Self::Workload,
            Self::Workload => Self::Reviews,
            Self::Reviews => Self::Active,
            Self::Active => Self::Overlap,
            Self::Overlap => Self::Expert,
        }
    }

    /// All modes in order.
    pub const ALL: [Self; 5] = [
        Self::Expert,
        Self::Workload,
        Self::Reviews,
        Self::Active,
        Self::Overlap,
    ];

    /// Mode from 1-based number key (1=Expert, 2=Workload, ..., 5=Overlap).
    #[must_use]
    pub fn from_number(n: u8) -> Option<Self> {
        match n {
            1 => Some(Self::Expert),
            2 => Some(Self::Workload),
            3 => Some(Self::Reviews),
            4 => Some(Self::Active),
            5 => Some(Self::Overlap),
            _ => None,
        }
    }
}

// ---------------------------------------------------------------------------
// WhoState
// ---------------------------------------------------------------------------

/// State for the who/people screen.
#[derive(Debug, Default)]
pub struct WhoState {
    /// Active query mode.
    pub mode: WhoMode,
    /// Current result (if any).
    pub result: Option<WhoResult>,

    // Input fields.
    /// Path input text (used by Expert and Overlap modes).
    pub path: String,
    /// Cursor position within path string (byte offset).
    pub path_cursor: usize,
    /// Whether the path input has focus.
    pub path_focused: bool,

    /// Username input text (used by Workload and Reviews modes).
    pub username: String,
    /// Cursor position within username string (byte offset).
    pub username_cursor: usize,
    /// Whether the username input has focus.
    pub username_focused: bool,

    /// Toggle: include closed entities in Workload/Active queries.
    pub include_closed: bool,

    // Result navigation.
    /// Index of the selected row in the result list.
    pub selected_index: usize,
    /// Vertical scroll offset for the result area.
    pub scroll_offset: usize,

    // Async coordination.
    /// Monotonic generation counter for stale-response detection.
    pub generation: u64,
    /// Whether a query is in-flight.
    pub loading: bool,
}

impl WhoState {
    /// Enter the who screen: focus the appropriate input.
    pub fn enter(&mut self) {
        self.focus_input_for_mode();
    }

    /// Leave the who screen: blur all inputs.
    pub fn leave(&mut self) {
        self.path_focused = false;
        self.username_focused = false;
    }

    /// Switch to a different mode. Clears result and resets selection.
    /// Returns the new generation for stale detection.
    pub fn set_mode(&mut self, mode: WhoMode) -> u64 {
        if self.mode == mode {
            return self.generation;
        }
        self.mode = mode;
        self.result = None;
        self.selected_index = 0;
        self.scroll_offset = 0;
        self.focus_input_for_mode();
        self.bump_generation()
    }

    /// Toggle include_closed. Returns new generation if the mode is affected.
    pub fn toggle_include_closed(&mut self) -> Option<u64> {
        self.include_closed = !self.include_closed;
        if self.mode.affected_by_include_closed() {
            Some(self.bump_generation())
        } else {
            None
        }
    }

    /// Apply query results if generation matches.
    pub fn apply_results(&mut self, generation: u64, result: WhoResult) {
        if generation != self.generation {
            return; // Stale response — discard.
        }
        self.result = Some(result);
        self.loading = false;
        self.selected_index = 0;
        self.scroll_offset = 0;
    }

    /// Submit the current input (trigger a query).
    /// Returns the generation for the new query.
    pub fn submit(&mut self) -> u64 {
        self.loading = true;
        self.bump_generation()
    }

    // --- Input field operations ---

    /// Insert a char at cursor in the active input field.
    pub fn insert_char(&mut self, c: char) {
        if self.path_focused {
            self.path.insert(self.path_cursor, c);
            self.path_cursor += c.len_utf8();
        } else if self.username_focused {
            self.username.insert(self.username_cursor, c);
            self.username_cursor += c.len_utf8();
        }
    }

    /// Delete the char before cursor in the active input field.
    pub fn delete_char_before_cursor(&mut self) {
        if self.path_focused && self.path_cursor > 0 {
            let prev = prev_char_boundary(&self.path, self.path_cursor);
            self.path.drain(prev..self.path_cursor);
            self.path_cursor = prev;
        } else if self.username_focused && self.username_cursor > 0 {
            let prev = prev_char_boundary(&self.username, self.username_cursor);
            self.username.drain(prev..self.username_cursor);
            self.username_cursor = prev;
        }
    }

    /// Move cursor left in the active input.
    pub fn cursor_left(&mut self) {
        if self.path_focused && self.path_cursor > 0 {
            self.path_cursor = prev_char_boundary(&self.path, self.path_cursor);
        } else if self.username_focused && self.username_cursor > 0 {
            self.username_cursor = prev_char_boundary(&self.username, self.username_cursor);
        }
    }

    /// Move cursor right in the active input.
    pub fn cursor_right(&mut self) {
        if self.path_focused && self.path_cursor < self.path.len() {
            self.path_cursor = next_char_boundary(&self.path, self.path_cursor);
        } else if self.username_focused && self.username_cursor < self.username.len() {
            self.username_cursor = next_char_boundary(&self.username, self.username_cursor);
        }
    }

    /// Whether any input field has focus.
    #[must_use]
    pub fn has_text_focus(&self) -> bool {
        self.path_focused || self.username_focused
    }

    /// Blur all inputs.
    pub fn blur(&mut self) {
        self.path_focused = false;
        self.username_focused = false;
    }

    /// Focus the appropriate input for the current mode.
    pub fn focus_input_for_mode(&mut self) {
        self.path_focused = self.mode.needs_path();
        self.username_focused = self.mode.needs_username();
        // Place cursor at end of text.
        if self.path_focused {
            self.path_cursor = self.path.len();
        }
        if self.username_focused {
            self.username_cursor = self.username.len();
        }
    }

    // --- Selection navigation ---

    /// Move selection up.
    pub fn select_prev(&mut self) {
        self.selected_index = self.selected_index.saturating_sub(1);
    }

    /// Move selection down (bounded by result count).
    pub fn select_next(&mut self, result_count: usize) {
        if result_count > 0 {
            self.selected_index = (self.selected_index + 1).min(result_count - 1);
        }
    }

    /// Ensure the selected row is visible within the viewport.
    pub fn ensure_visible(&mut self, viewport_height: usize) {
        if viewport_height == 0 {
            return;
        }
        if self.selected_index < self.scroll_offset {
            self.scroll_offset = self.selected_index;
        } else if self.selected_index >= self.scroll_offset + viewport_height {
            self.scroll_offset = self.selected_index - viewport_height + 1;
        }
    }

    // --- Internal ---

    fn bump_generation(&mut self) -> u64 {
        self.generation += 1;
        self.generation
    }
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

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

    #[test]
    fn test_who_mode_defaults_to_expert() {
        assert_eq!(WhoMode::default(), WhoMode::Expert);
    }

    #[test]
    fn test_who_mode_labels() {
        assert_eq!(WhoMode::Expert.label(), "Expert");
        assert_eq!(WhoMode::Active.label(), "Active");
        assert_eq!(WhoMode::Overlap.label(), "Overlap");
    }

    #[test]
    fn test_who_mode_needs_path() {
        assert!(WhoMode::Expert.needs_path());
        assert!(WhoMode::Overlap.needs_path());
        assert!(!WhoMode::Workload.needs_path());
        assert!(!WhoMode::Reviews.needs_path());
        assert!(!WhoMode::Active.needs_path());
    }

    #[test]
    fn test_who_mode_needs_username() {
        assert!(WhoMode::Workload.needs_username());
        assert!(WhoMode::Reviews.needs_username());
        assert!(!WhoMode::Expert.needs_username());
        assert!(!WhoMode::Active.needs_username());
    }

    #[test]
    fn test_who_mode_next_cycles() {
        let start = WhoMode::Expert;
        let m = start.next().next().next().next().next();
        assert_eq!(m, start);
    }

    #[test]
    fn test_who_mode_from_number() {
        assert_eq!(WhoMode::from_number(1), Some(WhoMode::Expert));
        assert_eq!(WhoMode::from_number(5), Some(WhoMode::Overlap));
        assert_eq!(WhoMode::from_number(0), None);
        assert_eq!(WhoMode::from_number(6), None);
    }

    #[test]
    fn test_who_state_default() {
        let state = WhoState::default();
        assert_eq!(state.mode, WhoMode::Expert);
        assert!(state.result.is_none());
        assert!(!state.include_closed);
        assert_eq!(state.generation, 0);
    }

    #[test]
    fn test_set_mode_bumps_generation() {
        let mut state = WhoState::default();
        let generation = state.set_mode(WhoMode::Workload);
        assert_eq!(generation, 1);
        assert_eq!(state.mode, WhoMode::Workload);
        assert!(state.result.is_none());
        assert!(state.username_focused);
        assert!(!state.path_focused);
    }

    #[test]
    fn test_set_mode_same_does_not_bump() {
        let mut state = WhoState::default();
        let generation = state.set_mode(WhoMode::Expert);
        assert_eq!(generation, 0); // No bump for same mode.
    }

    #[test]
    fn test_toggle_include_closed_returns_gen_for_affected_modes() {
        let state = &mut WhoState {
            mode: WhoMode::Workload,
            ..WhoState::default()
        };
        let generation = state.toggle_include_closed();
        assert!(generation.is_some());
        assert!(state.include_closed);
    }

    #[test]
    fn test_toggle_include_closed_returns_none_for_unaffected_modes() {
        let state = &mut WhoState {
            mode: WhoMode::Expert,
            ..WhoState::default()
        };
        let generation = state.toggle_include_closed();
        assert!(generation.is_none());
        assert!(state.include_closed);
    }

    #[test]
    fn test_stale_response_guard() {
        let mut state = WhoState::default();
        let stale_gen = state.submit();
        // Bump generation again (simulating user changed mode).
        let _new_gen = state.set_mode(WhoMode::Active);
        // Old response arrives — should be discarded.
        state.apply_results(
            stale_gen,
            WhoResult::Active(lore::core::who_types::ActiveResult {
                discussions: vec![],
                total_unresolved_in_window: 0,
                truncated: false,
            }),
        );
        assert!(state.result.is_none()); // Stale, discarded.
    }

    #[test]
    fn test_insert_and_delete_char() {
        let mut state = WhoState {
            path_focused: true,
            ..WhoState::default()
        };
        state.insert_char('s');
        state.insert_char('r');
        state.insert_char('c');
        assert_eq!(state.path, "src");
        assert_eq!(state.path_cursor, 3);

        state.delete_char_before_cursor();
        assert_eq!(state.path, "sr");
        assert_eq!(state.path_cursor, 2);
    }

    #[test]
    fn test_cursor_movement() {
        let mut state = WhoState {
            username_focused: true,
            username: "alice".into(),
            username_cursor: 5,
            ..WhoState::default()
        };

        state.cursor_left();
        assert_eq!(state.username_cursor, 4);
        state.cursor_right();
        assert_eq!(state.username_cursor, 5);
        // Right at end is clamped.
        state.cursor_right();
        assert_eq!(state.username_cursor, 5);
    }

    #[test]
    fn test_select_prev_next() {
        let mut state = WhoState::default();
        state.select_next(5);
        assert_eq!(state.selected_index, 1);
        state.select_next(5);
        assert_eq!(state.selected_index, 2);
        state.select_prev();
        assert_eq!(state.selected_index, 1);
        state.select_prev();
        assert_eq!(state.selected_index, 0);
        state.select_prev(); // Should not underflow.
        assert_eq!(state.selected_index, 0);
    }

    #[test]
    fn test_ensure_visible() {
        let mut state = WhoState {
            selected_index: 15,
            ..WhoState::default()
        };
        state.ensure_visible(5);
        assert_eq!(state.scroll_offset, 11); // 15 - 5 + 1
    }

    #[test]
    fn test_enter_focuses_correct_input() {
        let mut state = WhoState {
            mode: WhoMode::Expert,
            ..WhoState::default()
        };
        state.enter();
        assert!(state.path_focused);
        assert!(!state.username_focused);

        state.mode = WhoMode::Reviews;
        state.enter();
        assert!(!state.path_focused);
        // Reviews needs username.
        // focus_input_for_mode is called in enter().
    }

    #[test]
    fn test_affected_by_include_closed() {
        assert!(WhoMode::Workload.affected_by_include_closed());
        assert!(WhoMode::Active.affected_by_include_closed());
        assert!(!WhoMode::Expert.affected_by_include_closed());
        assert!(!WhoMode::Reviews.affected_by_include_closed());
        assert!(!WhoMode::Overlap.affected_by_include_closed());
    }
}
