#![allow(dead_code)] // Phase 2: consumed by Issue Detail screen

//! Issue detail screen state.
//!
//! Holds metadata, discussions, cross-references, and UI state for
//! viewing a single issue. Supports progressive hydration: metadata
//! loads first, discussions load async in a second phase.

use crate::message::EntityKey;
use crate::view::common::cross_ref::{CrossRef, CrossRefState};
use crate::view::common::discussion_tree::{DiscussionNode, DiscussionTreeState};

// ---------------------------------------------------------------------------
// IssueMetadata
// ---------------------------------------------------------------------------

/// Full metadata for a single issue, fetched from the local DB.
#[derive(Debug, Clone)]
pub struct IssueMetadata {
    /// Issue IID (project-scoped).
    pub iid: i64,
    /// Project path (e.g., "group/project").
    pub project_path: String,
    /// Issue title.
    pub title: String,
    /// Issue description (markdown).
    pub description: String,
    /// Current state: "opened" or "closed".
    pub state: String,
    /// Author username.
    pub author: String,
    /// Assigned usernames.
    pub assignees: Vec<String>,
    /// Label names.
    pub labels: Vec<String>,
    /// Milestone title (if set).
    pub milestone: Option<String>,
    /// Due date (if set, "YYYY-MM-DD").
    pub due_date: Option<String>,
    /// Created timestamp (ms epoch).
    pub created_at: i64,
    /// Updated timestamp (ms epoch).
    pub updated_at: i64,
    /// GitLab web URL for "open in browser".
    pub web_url: String,
    /// Discussion count (for display before discussions load).
    pub discussion_count: usize,
}

// ---------------------------------------------------------------------------
// IssueDetailData
// ---------------------------------------------------------------------------

/// Bundle returned by the metadata fetch action.
///
/// Metadata + cross-refs load in Phase 1 (fast). Discussions load
/// separately in Phase 2.
#[derive(Debug, Clone)]
pub struct IssueDetailData {
    pub metadata: IssueMetadata,
    pub cross_refs: Vec<CrossRef>,
}

// ---------------------------------------------------------------------------
// DetailSection
// ---------------------------------------------------------------------------

/// Which section of the detail view has keyboard focus.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DetailSection {
    /// Description area (scrollable text).
    #[default]
    Description,
    /// Discussion tree.
    Discussions,
    /// Cross-references list.
    CrossRefs,
}

impl DetailSection {
    /// Cycle to the next section.
    #[must_use]
    pub fn next(self) -> Self {
        match self {
            Self::Description => Self::Discussions,
            Self::Discussions => Self::CrossRefs,
            Self::CrossRefs => Self::Description,
        }
    }

    /// Cycle to the previous section.
    #[must_use]
    pub fn prev(self) -> Self {
        match self {
            Self::Description => Self::CrossRefs,
            Self::Discussions => Self::Description,
            Self::CrossRefs => Self::Discussions,
        }
    }
}

// ---------------------------------------------------------------------------
// IssueDetailState
// ---------------------------------------------------------------------------

/// State for the issue detail screen.
#[derive(Debug, Default)]
pub struct IssueDetailState {
    /// Entity key for the currently displayed issue.
    pub current_key: Option<EntityKey>,
    /// Issue metadata (Phase 1 load).
    pub metadata: Option<IssueMetadata>,
    /// Discussion nodes (Phase 2 async load).
    pub discussions: Vec<DiscussionNode>,
    /// Whether discussions have finished loading.
    pub discussions_loaded: bool,
    /// Cross-references (loaded with metadata in Phase 1).
    pub cross_refs: Vec<CrossRef>,
    /// Discussion tree UI state (expand/collapse, selection).
    pub tree_state: DiscussionTreeState,
    /// Cross-reference list UI state.
    pub cross_ref_state: CrossRefState,
    /// Description scroll offset.
    pub description_scroll: usize,
    /// Active section for keyboard focus.
    pub active_section: DetailSection,
}

impl IssueDetailState {
    /// Reset state for a new issue.
    pub fn load_new(&mut self, key: EntityKey) {
        self.current_key = Some(key);
        self.metadata = None;
        self.discussions.clear();
        self.discussions_loaded = false;
        self.cross_refs.clear();
        self.tree_state = DiscussionTreeState::default();
        self.cross_ref_state = CrossRefState::default();
        self.description_scroll = 0;
        self.active_section = DetailSection::Description;
    }

    /// Apply Phase 1 data (metadata + cross-refs).
    pub fn apply_metadata(&mut self, data: IssueDetailData) {
        self.metadata = Some(data.metadata);
        self.cross_refs = data.cross_refs;
    }

    /// Apply Phase 2 data (discussions).
    pub fn apply_discussions(&mut self, discussions: Vec<DiscussionNode>) {
        self.discussions = discussions;
        self.discussions_loaded = true;
    }

    /// Whether we have metadata loaded for the current key.
    #[must_use]
    pub fn has_metadata(&self) -> bool {
        self.metadata.is_some()
    }

    /// Cycle to the next section.
    pub fn next_section(&mut self) {
        self.active_section = self.active_section.next();
    }

    /// Cycle to the previous section.
    pub fn prev_section(&mut self) {
        self.active_section = self.active_section.prev();
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use crate::view::common::cross_ref::CrossRefKind;

    #[test]
    fn test_issue_detail_state_default() {
        let state = IssueDetailState::default();
        assert!(state.current_key.is_none());
        assert!(state.metadata.is_none());
        assert!(state.discussions.is_empty());
        assert!(!state.discussions_loaded);
        assert!(state.cross_refs.is_empty());
        assert_eq!(state.active_section, DetailSection::Description);
    }

    #[test]
    fn test_load_new_resets_state() {
        let mut state = IssueDetailState {
            discussions_loaded: true,
            description_scroll: 10,
            active_section: DetailSection::CrossRefs,
            ..IssueDetailState::default()
        };

        state.load_new(EntityKey::issue(1, 42));
        assert_eq!(state.current_key, Some(EntityKey::issue(1, 42)));
        assert!(state.metadata.is_none());
        assert!(!state.discussions_loaded);
        assert_eq!(state.description_scroll, 0);
        assert_eq!(state.active_section, DetailSection::Description);
    }

    #[test]
    fn test_apply_metadata() {
        let mut state = IssueDetailState::default();
        state.load_new(EntityKey::issue(1, 42));

        let data = IssueDetailData {
            metadata: IssueMetadata {
                iid: 42,
                project_path: "group/proj".into(),
                title: "Fix auth".into(),
                description: "Description here".into(),
                state: "opened".into(),
                author: "alice".into(),
                assignees: vec!["bob".into()],
                labels: vec!["backend".into()],
                milestone: Some("v1.0".into()),
                due_date: None,
                created_at: 1_700_000_000_000,
                updated_at: 1_700_000_060_000,
                web_url: "https://gitlab.com/group/proj/-/issues/42".into(),
                discussion_count: 3,
            },
            cross_refs: vec![CrossRef {
                kind: CrossRefKind::ClosingMr,
                entity_key: EntityKey::mr(1, 10),
                label: "Fix auth MR".into(),
                navigable: true,
            }],
        };

        state.apply_metadata(data);
        assert!(state.has_metadata());
        assert_eq!(state.metadata.as_ref().unwrap().iid, 42);
        assert_eq!(state.cross_refs.len(), 1);
    }

    #[test]
    fn test_apply_discussions() {
        let mut state = IssueDetailState::default();
        assert!(!state.discussions_loaded);

        let discussions = vec![DiscussionNode {
            discussion_id: "d1".into(),
            notes: vec![],
            resolvable: false,
            resolved: false,
        }];

        state.apply_discussions(discussions);
        assert!(state.discussions_loaded);
        assert_eq!(state.discussions.len(), 1);
    }

    #[test]
    fn test_detail_section_cycling() {
        let section = DetailSection::Description;
        assert_eq!(section.next(), DetailSection::Discussions);
        assert_eq!(section.next().next(), DetailSection::CrossRefs);
        assert_eq!(section.next().next().next(), DetailSection::Description);

        assert_eq!(section.prev(), DetailSection::CrossRefs);
        assert_eq!(section.prev().prev(), DetailSection::Discussions);
    }

    #[test]
    fn test_section_next_prev_round_trip() {
        let mut state = IssueDetailState::default();
        assert_eq!(state.active_section, DetailSection::Description);

        state.next_section();
        assert_eq!(state.active_section, DetailSection::Discussions);

        state.prev_section();
        assert_eq!(state.active_section, DetailSection::Description);
    }
}
