//! Boundary error values

use serde::{Deserialize, Serialize};

use super::text::{ErrorHelp, ErrorMessage, ErrorPath, ErrorTag};

/// Fixed error value sent over service boundaries
#[derive(
    Clone,
    Debug,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
pub struct WireError {
    /// Transport-neutral error class
    pub kind: WireErrorKind,
    /// Stable namespaced code
    pub tag: ErrorTag,
    /// Caller-safe failure message
    pub message: ErrorMessage,
    /// Caller-safe recovery hint
    pub help: ErrorHelp,
    /// Request field or resource path
    pub path: Option<ErrorPath>,
    /// Retry behavior for the caller
    pub retry: RetryAdvice,
}

impl WireError {
    pub fn new(kind: WireErrorKind, tag: ErrorTag, message: ErrorMessage, help: ErrorHelp) -> Self {
        Self {
            kind,
            tag,
            message,
            help,
            path: None,
            retry: RetryAdvice::Never,
        }
    }

    pub fn with_path(mut self, path: ErrorPath) -> Self {
        self.path = Some(path);
        self
    }

    pub fn with_retry(mut self, retry: RetryAdvice) -> Self {
        self.retry = retry;
        self
    }
}

/// Transport-neutral error class
#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Hash,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WireErrorKind {
    /// Request syntax or typed value is invalid
    InvalidArgument,
    /// Caller is not authenticated
    Unauthenticated,
    /// Caller is authenticated but not allowed
    PermissionDenied,
    /// Resource does not exist
    NotFound,
    /// Resource already exists
    AlreadyExists,
    /// Request conflicts with current state
    Conflict,
    /// Deadline expired before completion
    DeadlineExceeded,
    /// Capacity, quota, or memory limit was reached
    ResourceExhausted,
    /// Dependency or service is unavailable
    Unavailable,
    /// Operation is not implemented
    Unimplemented,
    /// Internal failure with no safe detail
    Internal,
}

/// Retry behavior for a failed boundary call
#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Hash,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum RetryAdvice {
    /// Do not retry without changing the request or state
    Never,
    /// Retry is safe for idempotent callers
    Safe,
    /// Retry after backoff or an explicit delay
    After,
}
