// Phase 0: Portal Monitoring State Management
// Structures for section-based, interactive monitoring

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::{Duration, Instant};

/// Main sections of the monitoring view
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MonitoringSection {
    Overview,
    Health,
    Dependencies,
    Metrics,
    Queue,
    Logs,
    System,
}

impl MonitoringSection {
    pub fn all() -> Vec<Self> {
        vec![
            Self::Overview,
            Self::Health,
            Self::Dependencies,
            Self::Metrics,
            Self::Queue,
            Self::Logs,
            Self::System,
        ]
    }

    pub fn display_name(&self) -> &'static str {
        match self {
            Self::Overview => "Overview",
            Self::Health => "Health",
            Self::Dependencies => "Dependencies",
            Self::Metrics => "Metrics",
            Self::Queue => "Queue",
            Self::Logs => "Logs",
            Self::System => "System",
        }
    }

    /// Lowercase slug sent on `monitoring.refresh` (`section` field).
    pub fn cli_slug(&self) -> &'static str {
        match self {
            Self::Overview => "overview",
            Self::Health => "health",
            Self::Dependencies => "dependencies",
            Self::Metrics => "metrics",
            Self::Queue => "queue",
            Self::Logs => "logs",
            Self::System => "system",
        }
    }

    pub fn from_cli_slug(s: &str) -> Option<Self> {
        let t = s.trim().to_lowercase();
        match t.as_str() {
            "overview" => Some(Self::Overview),
            "health" => Some(Self::Health),
            "dependencies" => Some(Self::Dependencies),
            "metrics" => Some(Self::Metrics),
            "queue" => Some(Self::Queue),
            "logs" => Some(Self::Logs),
            "system" => Some(Self::System),
            _ => None,
        }
    }
}

/// Phase 3: Metrics section drill targets (`monitoring.drill` `target` field).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MetricsDrillPanel {
    Http,
    Runs,
    Queue,
    Sse,
}

impl MetricsDrillPanel {
    pub fn next(self) -> Self {
        match self {
            Self::Http => Self::Runs,
            Self::Runs => Self::Queue,
            Self::Queue => Self::Sse,
            Self::Sse => Self::Http,
        }
    }

    pub fn cli_target(&self) -> &'static str {
        match self {
            Self::Http => "metrics_http",
            Self::Runs => "metrics_runs",
            Self::Queue => "metrics_queue",
            Self::Sse => "metrics_sse",
        }
    }

    pub fn label(&self) -> &'static str {
        match self {
            Self::Http => "HTTP",
            Self::Runs => "Runs",
            Self::Queue => "Queue",
            Self::Sse => "SSE",
        }
    }

    pub fn from_cli_target(s: &str) -> Option<Self> {
        match s.trim() {
            "metrics_http" => Some(Self::Http),
            "metrics_runs" => Some(Self::Runs),
            "metrics_queue" => Some(Self::Queue),
            "metrics_sse" => Some(Self::Sse),
            _ => None,
        }
    }
}

/// Status indicator for sections (determines color)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SectionStatus {
    Healthy,   // Green
    Degraded,  // Yellow
    Unhealthy, // Red
    Unknown,   // Gray
    Loading,   // Blue/Cyan
}

impl SectionStatus {
    pub fn symbol(&self) -> &'static str {
        match self {
            Self::Healthy => "●",
            Self::Degraded => "⚠",
            Self::Unhealthy => "✖",
            Self::Unknown => "○",
            Self::Loading => "◉",
        }
    }
}

/// State for each individual section.
///
/// **Phase 1 Portal Monitoring:** only `expanded` is authoritative for the UI. Status/summary strings
/// here are not driven by `portal_monitoring` render (that path recomputes from the CLI snapshot each
/// frame). Future phases may use `update_section` to populate these; until then do not assume headers
/// reflect `SectionState::status` / `summary`.
#[derive(Debug, Clone)]
pub struct SectionState {
    /// Whether section is expanded (shows detail) or collapsed (shows summary)
    pub expanded: bool,
    /// Current status (for color coding header)
    pub status: SectionStatus,
    /// One-line summary shown when collapsed
    pub summary: String,
    /// When this section was last refreshed
    pub last_refresh: Option<Instant>,
    /// Whether section is currently loading new data
    pub loading: bool,
    /// Error message if last refresh failed
    pub error: Option<String>,
    /// Arbitrary data payload (parsed from Gateway response)
    pub data: Option<serde_json::Value>,
}

impl Default for SectionState {
    fn default() -> Self {
        Self {
            expanded: false,
            status: SectionStatus::Unknown,
            summary: String::new(),
            last_refresh: None,
            loading: false,
            error: None,
            data: None,
        }
    }
}

/// Main monitoring state (Phase 0 foundation).
///
/// **Phase 1:** `selected_index` / `selected_section` and `sections[..].expanded` drive the TUI;
/// per-section `SectionState::status` / `summary` are not used for drawing (see `build_body_lines` in
/// `ui/portal_monitoring.rs` — status and one-line summaries come from `parse_snapshot` only).
#[derive(Debug, Clone)]
pub struct MonitoringState {
    /// Currently selected section (for keyboard navigation)
    pub selected_section: Option<MonitoringSection>,
    /// Index in the section list (0 = Overview, 1 = Health, etc.)
    pub selected_index: usize,
    /// Per-section state
    pub sections: HashMap<MonitoringSection, SectionState>,
    /// Auto-refresh enabled/disabled
    pub live_mode: bool,
    /// Auto-refresh interval (default 5s)
    pub refresh_interval: Duration,
    /// When auto-refresh last triggered
    pub last_auto_refresh: Option<Instant>,
    /// Gateway URL we're monitoring
    pub gateway_url: Option<String>,
}

impl Default for MonitoringState {
    fn default() -> Self {
        let mut sections = HashMap::new();
        for section in MonitoringSection::all() {
            sections.insert(section, SectionState::default());
        }

        Self {
            selected_section: Some(MonitoringSection::Overview),
            selected_index: 0,
            sections,
            live_mode: false, // Start with auto-refresh off
            refresh_interval: Duration::from_secs(5),
            last_auto_refresh: None,
            gateway_url: None,
        }
    }
}

impl MonitoringState {
    pub fn new(gateway_url: Option<String>) -> Self {
        let mut state = Self::default();
        state.gateway_url = gateway_url;
        state
    }

    /// Get the currently selected section
    pub fn current_section(&self) -> Option<MonitoringSection> {
        self.selected_section
    }

    /// Move selection up (previous section)
    pub fn select_previous(&mut self) {
        if self.selected_index > 0 {
            self.selected_index -= 1;
            self.selected_section = Some(MonitoringSection::all()[self.selected_index]);
        }
    }

    /// Move selection down (next section)
    pub fn select_next(&mut self) {
        let sections = MonitoringSection::all();
        if self.selected_index < sections.len() - 1 {
            self.selected_index += 1;
            self.selected_section = Some(sections[self.selected_index]);
        }
    }

    /// Toggle expand/collapse for currently selected section
    pub fn toggle_current_section(&mut self) {
        if let Some(section) = self.selected_section {
            if let Some(state) = self.sections.get_mut(&section) {
                state.expanded = !state.expanded;
            }
        }
    }

    /// Toggle expand/collapse for the section at `index` (Phase 1 Portal Monitoring).
    pub fn toggle_section_at_index(&mut self, index: usize) {
        let sections = MonitoringSection::all();
        if let Some(&section) = sections.get(index) {
            if let Some(state) = self.sections.get_mut(&section) {
                state.expanded = !state.expanded;
            }
        }
    }

    /// Move selection with wrap (Phase 1).
    pub fn select_previous_wrapped(&mut self) {
        let n = MonitoringSection::all().len();
        if n == 0 {
            return;
        }
        self.selected_index = (self.selected_index + n - 1) % n;
        self.selected_section = MonitoringSection::all().get(self.selected_index).copied();
    }

    pub fn select_next_wrapped(&mut self) {
        let n = MonitoringSection::all().len();
        if n == 0 {
            return;
        }
        self.selected_index = (self.selected_index + 1) % n;
        self.selected_section = MonitoringSection::all().get(self.selected_index).copied();
    }

    /// Expand a specific section
    pub fn expand_section(&mut self, section: MonitoringSection) {
        if let Some(state) = self.sections.get_mut(&section) {
            state.expanded = true;
        }
    }

    /// Move keyboard focus directly to a specific section.
    pub fn select_section(&mut self, section: MonitoringSection) {
        if let Some(index) = MonitoringSection::all()
            .iter()
            .position(|candidate| *candidate == section)
        {
            self.selected_index = index;
            self.selected_section = Some(section);
        }
    }

    /// Collapse a specific section
    pub fn collapse_section(&mut self, section: MonitoringSection) {
        if let Some(state) = self.sections.get_mut(&section) {
            state.expanded = false;
        }
    }

    /// Collapse all sections
    pub fn collapse_all(&mut self) {
        for state in self.sections.values_mut() {
            state.expanded = false;
        }
    }

    /// Toggle auto-refresh (live mode)
    pub fn toggle_live_mode(&mut self) {
        self.live_mode = !self.live_mode;
        if self.live_mode {
            self.last_auto_refresh = Some(Instant::now());
        }
    }

    /// Check if it's time for auto-refresh
    pub fn should_auto_refresh(&self) -> bool {
        if !self.live_mode {
            return false;
        }
        match self.last_auto_refresh {
            Some(last) => last.elapsed() >= self.refresh_interval,
            None => true,
        }
    }

    /// Mark that auto-refresh just happened
    pub fn mark_auto_refresh(&mut self) {
        self.last_auto_refresh = Some(Instant::now());
    }

    /// Set loading state for a section
    pub fn set_loading(&mut self, section: MonitoringSection, loading: bool) {
        if let Some(state) = self.sections.get_mut(&section) {
            state.loading = loading;
            if loading {
                state.status = SectionStatus::Loading;
                state.error = None;
            }
        }
    }

    /// Update section data after successful refresh
    pub fn update_section(
        &mut self,
        section: MonitoringSection,
        status: SectionStatus,
        summary: String,
        data: Option<serde_json::Value>,
    ) {
        if let Some(state) = self.sections.get_mut(&section) {
            state.status = status;
            state.summary = summary;
            state.data = data;
            state.last_refresh = Some(Instant::now());
            state.loading = false;
            state.error = None;
        }
    }

    /// Set error state for a section
    pub fn set_error(&mut self, section: MonitoringSection, error: String) {
        if let Some(state) = self.sections.get_mut(&section) {
            state.status = SectionStatus::Unhealthy;
            state.error = Some(error);
            state.loading = false;
        }
    }

    /// Get section state (immutable)
    pub fn get_section(&self, section: MonitoringSection) -> Option<&SectionState> {
        self.sections.get(&section)
    }

    /// Get time since last refresh for a section (for display)
    pub fn time_since_refresh(&self, section: MonitoringSection) -> Option<Duration> {
        self.sections
            .get(&section)
            .and_then(|s| s.last_refresh)
            .map(|t| t.elapsed())
    }
}

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

    #[test]
    fn test_section_navigation() {
        let mut state = MonitoringState::default();
        assert_eq!(state.selected_index, 0);
        assert_eq!(state.current_section(), Some(MonitoringSection::Overview));

        state.select_next();
        assert_eq!(state.selected_index, 1);
        assert_eq!(state.current_section(), Some(MonitoringSection::Health));

        state.select_previous();
        assert_eq!(state.selected_index, 0);
        assert_eq!(state.current_section(), Some(MonitoringSection::Overview));
    }

    #[test]
    fn test_toggle_expansion() {
        let mut state = MonitoringState::default();
        state.selected_section = Some(MonitoringSection::Health);

        let health = state.get_section(MonitoringSection::Health).unwrap();
        assert!(!health.expanded);

        state.toggle_current_section();
        let health = state.get_section(MonitoringSection::Health).unwrap();
        assert!(health.expanded);
    }

    #[test]
    fn test_live_mode() {
        let mut state = MonitoringState::default();
        assert!(!state.live_mode);

        state.toggle_live_mode();
        assert!(state.live_mode);
        assert!(state.last_auto_refresh.is_some());
    }
}
