#![allow(dead_code)]

//! Dashboard screen state.
//!
//! The dashboard is the home screen — entity counts, per-project sync
//! status, recent activity, and the last sync summary.

// ---------------------------------------------------------------------------
// EntityCounts
// ---------------------------------------------------------------------------

/// Aggregated entity counts from the local database.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EntityCounts {
    pub issues_open: u64,
    pub issues_total: u64,
    pub mrs_open: u64,
    pub mrs_total: u64,
    pub discussions: u64,
    pub notes_total: u64,
    /// Percentage of notes that are system-generated (0-100).
    pub notes_system_pct: u8,
    pub documents: u64,
    pub embeddings: u64,
}

// ---------------------------------------------------------------------------
// ProjectSyncInfo
// ---------------------------------------------------------------------------

/// Per-project sync freshness.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProjectSyncInfo {
    pub path: String,
    pub minutes_since_sync: u64,
}

// ---------------------------------------------------------------------------
// RecentActivityItem
// ---------------------------------------------------------------------------

/// A recently-updated entity for the activity feed.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RecentActivityItem {
    /// "issue" or "mr".
    pub entity_type: String,
    pub iid: u64,
    pub title: String,
    pub state: String,
    pub minutes_ago: u64,
}

// ---------------------------------------------------------------------------
// LastSyncInfo
// ---------------------------------------------------------------------------

/// Summary of the most recent sync run.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LastSyncInfo {
    pub status: String,
    /// Milliseconds epoch UTC.
    pub finished_at: Option<i64>,
    pub command: String,
    pub error: Option<String>,
}

// ---------------------------------------------------------------------------
// DashboardData
// ---------------------------------------------------------------------------

/// Data returned by the `fetch_dashboard` action.
///
/// Pure data transfer — no rendering or display logic.
#[derive(Debug, Clone, Default)]
pub struct DashboardData {
    pub counts: EntityCounts,
    pub projects: Vec<ProjectSyncInfo>,
    pub recent: Vec<RecentActivityItem>,
    pub last_sync: Option<LastSyncInfo>,
}

// ---------------------------------------------------------------------------
// DashboardState
// ---------------------------------------------------------------------------

/// State for the dashboard summary screen.
#[derive(Debug, Default)]
pub struct DashboardState {
    pub counts: EntityCounts,
    pub projects: Vec<ProjectSyncInfo>,
    pub recent: Vec<RecentActivityItem>,
    pub last_sync: Option<LastSyncInfo>,
    /// Scroll offset for the recent activity list.
    pub scroll_offset: usize,
}

impl DashboardState {
    /// Apply fresh data from a `fetch_dashboard` result.
    ///
    /// Preserves scroll offset (clamped to new data bounds).
    pub fn update(&mut self, data: DashboardData) {
        self.counts = data.counts;
        self.projects = data.projects;
        self.last_sync = data.last_sync;
        self.recent = data.recent;
        // Clamp scroll offset if the list shrunk.
        if !self.recent.is_empty() {
            self.scroll_offset = self.scroll_offset.min(self.recent.len() - 1);
        } else {
            self.scroll_offset = 0;
        }
    }

    /// Scroll the recent activity list down by one.
    pub fn scroll_down(&mut self) {
        if !self.recent.is_empty() {
            self.scroll_offset = (self.scroll_offset + 1).min(self.recent.len() - 1);
        }
    }

    /// Scroll the recent activity list up by one.
    pub fn scroll_up(&mut self) {
        self.scroll_offset = self.scroll_offset.saturating_sub(1);
    }
}

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

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

    #[test]
    fn test_dashboard_state_default() {
        let state = DashboardState::default();
        assert_eq!(state.counts.issues_total, 0);
        assert_eq!(state.scroll_offset, 0);
        assert!(state.recent.is_empty());
    }

    #[test]
    fn test_dashboard_state_update_applies_data() {
        let mut state = DashboardState::default();
        let data = DashboardData {
            counts: EntityCounts {
                issues_open: 3,
                issues_total: 5,
                ..Default::default()
            },
            projects: vec![ProjectSyncInfo {
                path: "group/project".into(),
                minutes_since_sync: 42,
            }],
            recent: vec![RecentActivityItem {
                entity_type: "issue".into(),
                iid: 1,
                title: "Fix bug".into(),
                state: "opened".into(),
                minutes_ago: 10,
            }],
            last_sync: None,
        };

        state.update(data);
        assert_eq!(state.counts.issues_open, 3);
        assert_eq!(state.counts.issues_total, 5);
        assert_eq!(state.projects.len(), 1);
        assert_eq!(state.recent.len(), 1);
    }

    #[test]
    fn test_dashboard_state_update_clamps_scroll() {
        let mut state = DashboardState {
            scroll_offset: 10,
            ..Default::default()
        };

        let data = DashboardData {
            recent: vec![RecentActivityItem {
                entity_type: "issue".into(),
                iid: 1,
                title: "Only item".into(),
                state: "opened".into(),
                minutes_ago: 5,
            }],
            ..Default::default()
        };

        state.update(data);
        assert_eq!(state.scroll_offset, 0); // Clamped to len-1 = 0
    }

    #[test]
    fn test_dashboard_state_update_empty_resets_scroll() {
        let mut state = DashboardState {
            scroll_offset: 5,
            ..Default::default()
        };

        state.update(DashboardData::default());
        assert_eq!(state.scroll_offset, 0);
    }

    #[test]
    fn test_scroll_down_and_up() {
        let mut state = DashboardState::default();
        state.recent = (0..5)
            .map(|i| RecentActivityItem {
                entity_type: "issue".into(),
                iid: i,
                title: format!("Item {i}"),
                state: "opened".into(),
                minutes_ago: i,
            })
            .collect();

        assert_eq!(state.scroll_offset, 0);
        state.scroll_down();
        assert_eq!(state.scroll_offset, 1);
        state.scroll_down();
        assert_eq!(state.scroll_offset, 2);
        state.scroll_up();
        assert_eq!(state.scroll_offset, 1);
        state.scroll_up();
        assert_eq!(state.scroll_offset, 0);
        state.scroll_up(); // Can't go below 0
        assert_eq!(state.scroll_offset, 0);
    }

    #[test]
    fn test_scroll_down_stops_at_end() {
        let mut state = DashboardState::default();
        state.recent = vec![RecentActivityItem {
            entity_type: "mr".into(),
            iid: 1,
            title: "Only".into(),
            state: "merged".into(),
            minutes_ago: 0,
        }];

        state.scroll_down();
        assert_eq!(state.scroll_offset, 0); // Can't scroll past single item
    }

    #[test]
    fn test_scroll_on_empty_is_noop() {
        let mut state = DashboardState::default();
        state.scroll_down();
        assert_eq!(state.scroll_offset, 0);
        state.scroll_up();
        assert_eq!(state.scroll_offset, 0);
    }
}
