//! Workspace error types.

use crate::capability::CapabilityMode;

/// Errors surfaced by the workspace public API.
///
/// `#[non_exhaustive]` so adding new variants is a non-breaking change.
/// Tests should match on variants rather than scrape the `Display` text.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum WorkspaceError {
    #[error("parent session not found: {0}")]
    ParentSessionNotFound(String),

    #[error("session not found: {0}")]
    SessionNotFound(String),

    #[error("session already exists: {0}")]
    SessionAlreadyExists(String),

    #[error("agent_id must be non-empty")]
    EmptyAgentId,

    #[error("the main session cannot be dropped")]
    CannotDropMainSession,

    #[error("toolset finalization failed: {0}")]
    Finalize(String),

    #[error("capability widening rejected: child {child:?} is not a subset of parent {parent:?}")]
    CapabilityWidening {
        parent: CapabilityMode,
        child: CapabilityMode,
    },

    #[error("session {caller:?} is not authorised to operate on session {target:?}")]
    Unauthorized { caller: String, target: String },

    /// A toolset mutation was rejected because the target session has an
    /// active turn. Retryable at the turn boundary (`after_turn`).
    #[error("turn active for session {0}; retry the tool-config update at the turn boundary")]
    TurnActive(String),

    #[error("maximum fork depth exceeded for parent session {parent:?}")]
    MaxDepthExceeded { parent: String },

    #[error("internal task failure: {0}")]
    JoinError(String),

    #[error("invalid hunk action: {0}")]
    InvalidHunkAction(String),

    #[error("hunk action failed: {0}")]
    HunkActionFailed(String),

    /// An internal workspace error.
    #[error("workspace error: {0}")]
    Internal(String),

    /// Deploy-service error tagged with its gRPC status class; see
    /// [`DeployError`] for how the class crosses the workspace RPC boundary.
    ///
    /// [`DeployError`]: kigi_workspace_types::rpc::deploy::DeployError
    #[error("deploy error: {message}")]
    DeployError {
        kind: kigi_workspace_types::rpc::deploy::DeployError,
        message: String,
    },

    /// The workspace is draining/shutting down and is no longer accepting new
    /// sessions. Surfaced when a `bind`/create races a terminal drain so the
    /// shared upload queue is never torn down out from under a fresh session.
    #[error("workspace is shutting down; not accepting new sessions")]
    ShuttingDown,

    /// The session's toolset is externally owned — installed by a local
    /// (shell) bind, its `Terminal` resource is not the session-owned
    /// backend — so an RPC-driven toolset mutation is refused instead of
    /// silently skipped. Hard error: retrying cannot succeed while the
    /// local bind holds the toolset.
    #[error("toolset externally owned (local bind), mutation refused: {0}")]
    ToolsetExternallyOwned(String),
}

/// Convenience alias for the workspace's primary `Result` type.
pub type WorkspaceResult<T> = Result<T, WorkspaceError>;
