use serde::{Deserialize, Serialize};

/// Scope level for workspace access granted to an MCP tool.
///
/// Tools declare which workspace scopes they require. The sandbox enforces
/// these scopes — a tool cannot access capabilities beyond its granted scope.
///
/// # Adding New Variants
///
/// This enum is `#[non_exhaustive]`. When adding a new variant, you MUST update
/// all three permission methods (`can_read`, `can_write`, `can_exec`) to handle it.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum WorkspaceScope {
    /// Read files from the agent's workspace.
    ReadOnly,
    /// Read and write files in the agent's workspace.
    ReadWrite,
    /// Execute commands in the agent's workspace.
    Exec,
}

impl WorkspaceScope {
    /// Returns `true` if this scope permits reading files.
    pub fn can_read(&self) -> bool {
        match self {
            Self::ReadOnly | Self::ReadWrite | Self::Exec => true,
        }
    }

    /// Returns `true` if this scope permits writing files.
    pub fn can_write(&self) -> bool {
        match self {
            Self::ReadWrite | Self::Exec => true,
            Self::ReadOnly => false,
        }
    }

    /// Returns `true` if this scope permits executing commands.
    pub fn can_exec(&self) -> bool {
        match self {
            Self::Exec => true,
            Self::ReadOnly | Self::ReadWrite => false,
        }
    }
}

impl std::fmt::Display for WorkspaceScope {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ReadOnly => write!(f, "read_only"),
            Self::ReadWrite => write!(f, "read_write"),
            Self::Exec => write!(f, "exec"),
        }
    }
}

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

    use super::*;

    #[rstest]
    #[case::read_only(WorkspaceScope::ReadOnly, true, false, false)]
    #[case::read_write(WorkspaceScope::ReadWrite, true, true, false)]
    #[case::exec(WorkspaceScope::Exec, true, true, true)]
    fn when_scope_checked_then_permissions_correct(
        #[case] scope: WorkspaceScope,
        #[case] can_read: bool,
        #[case] can_write: bool,
        #[case] can_exec: bool,
    ) {
        assert_eq!(scope.can_read(), can_read);
        assert_eq!(scope.can_write(), can_write);
        assert_eq!(scope.can_exec(), can_exec);
    }

    #[rstest]
    fn when_serialized_then_snake_case() {
        let json = serde_json::to_string(&WorkspaceScope::ReadOnly).expect("serialize");
        assert_eq!(json, "\"read_only\"");
    }

    #[rstest]
    fn when_deserialized_then_round_trips() {
        let scope: WorkspaceScope = serde_json::from_str("\"read_write\"").expect("deserialize");
        assert_eq!(scope, WorkspaceScope::ReadWrite);
    }

    #[rstest]
    fn when_display_then_matches_serde() {
        assert_eq!(WorkspaceScope::Exec.to_string(), "exec");
    }
}
