use anyhow::Result;
use regex::Regex;

#[derive(Debug, Clone)]
pub struct NoteSelector {
    pub workspace_slug: Option<String>,
    pub notebook_slug: Option<String>,
    pub note_slug: Option<String>,
    pub notebook_id: Option<String>,
    pub note_id: Option<String>,
}

impl NoteSelector {
    /// Parse a Doqium selector like:
    /// - @notebook[workspace/notebook]/note
    /// - @notebook[notebook]/note
    /// - @notebook/note
    /// - note-id (UUID)
    pub fn parse(selector: &str, default_notebook_id: Option<String>) -> Result<Self> {
        // Check if it's a UUID (note ID)
        if is_uuid(selector) {
            return Ok(NoteSelector {
                workspace_slug: None,
                notebook_slug: None,
                note_slug: None,
                notebook_id: default_notebook_id,
                note_id: Some(selector.to_string()),
            });
        }

        // Parse Doqium selector format
        let re = Regex::new(r"^@notebook(\[([^\]]+)\])?/([^#]+)(#.*)?$")?;

        if let Some(caps) = re.captures(selector) {
            let where_part = caps.get(2).map(|m| m.as_str());
            let note_part = caps.get(3).map(|m| m.as_str().to_string()).unwrap();

            let (workspace_slug, notebook_slug) = if let Some(where_str) = where_part {
                let parts: Vec<&str> = where_str.split('/').collect();
                match parts.len() {
                    2 => {
                        // workspace/notebook
                        (Some(parts[0].to_string()), Some(parts[1].to_string()))
                    }
                    1 => {
                        // notebook
                        (None, Some(parts[0].to_string()))
                    }
                    _ => {
                        anyhow::bail!("Invalid selector format: {}", selector);
                    }
                }
            } else {
                // @notebook/note (use default notebook)
                (None, None)
            };

            return Ok(NoteSelector {
                workspace_slug,
                notebook_slug,
                note_slug: Some(note_part),
                notebook_id: None,
                note_id: None,
            });
        }

        anyhow::bail!(
            "Invalid selector format: '{}'. Use @notebook[workspace/notebook]/note or a UUID",
            selector
        );
    }

    /// Check if this selector needs to be resolved (has slugs)
    pub fn needs_resolution(&self) -> bool {
        self.workspace_slug.is_some() || self.notebook_slug.is_some() || self.note_slug.is_some()
    }
}

fn is_uuid(s: &str) -> bool {
    let re = Regex::new(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$").unwrap();
    re.is_match(s)
}

/// Parse a notebook selector like:
/// - @notebook[workspace/notebook]
/// - @notebook[notebook]
/// - notebook-id (UUID)
pub fn parse_notebook_selector(
    selector: &str,
) -> Result<(Option<String>, Option<String>, Option<String>)> {
    // Check if it's a UUID (notebook ID)
    if is_uuid(selector) {
        return Ok((None, None, Some(selector.to_string())));
    }

    // Parse Doqium notebook selector format: @notebook[workspace/notebook] or @notebook[notebook]
    let re = Regex::new(r"^@notebook\[([^\]]+)\]$")?;

    if let Some(caps) = re.captures(selector) {
        let where_part = caps.get(1).map(|m| m.as_str()).unwrap();
        let parts: Vec<&str> = where_part.split('/').collect();

        match parts.len() {
            2 => {
                // workspace/notebook
                Ok((Some(parts[0].to_string()), Some(parts[1].to_string()), None))
            }
            1 => {
                // notebook only
                Ok((None, Some(parts[0].to_string()), None))
            }
            _ => {
                anyhow::bail!("Invalid notebook selector format: {}", selector)
            }
        }
    } else {
        anyhow::bail!(
            "Invalid notebook selector: {}. Expected @notebook[workspace/notebook], @notebook[notebook], or notebook-id",
            selector
        )
    }
}

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

    #[test]
    fn test_parse_uuid() {
        let selector = NoteSelector::parse("651e56d5-d1b9-4a6e-a775-7e5287083a78", None).unwrap();
        assert_eq!(
            selector.note_id,
            Some("651e56d5-d1b9-4a6e-a775-7e5287083a78".to_string())
        );
        assert!(selector.note_slug.is_none());
    }

    #[test]
    fn test_parse_workspace_notebook_note() {
        let selector = NoteSelector::parse("@notebook[default/my-notebook]/my-note", None).unwrap();
        assert_eq!(selector.workspace_slug, Some("default".to_string()));
        assert_eq!(selector.notebook_slug, Some("my-notebook".to_string()));
        assert_eq!(selector.note_slug, Some("my-note".to_string()));
    }

    #[test]
    fn test_parse_notebook_note() {
        let selector = NoteSelector::parse("@notebook[my-notebook]/my-note", None).unwrap();
        assert!(selector.workspace_slug.is_none());
        assert_eq!(selector.notebook_slug, Some("my-notebook".to_string()));
        assert_eq!(selector.note_slug, Some("my-note".to_string()));
    }

    #[test]
    fn test_parse_note_only() {
        let selector = NoteSelector::parse("@notebook/my-note", None).unwrap();
        assert!(selector.workspace_slug.is_none());
        assert!(selector.notebook_slug.is_none());
        assert_eq!(selector.note_slug, Some("my-note".to_string()));
    }
}
