//! Per-script overrides for the Rhai sandbox limits.
//!
//! All fields are optional: `None` means "use the executor's platform
//! default for this knob". Stored as JSONB in the `scripts.sandbox`
//! column so adding new knobs in the future is a code-only change.
//!
//! Values are clamped against the admin-set ceiling at write time
//! (in `manager-core`). The executor trusts what's stored: it merges
//! defaults + overrides per call and applies the result.

use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct ScriptSandbox {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_operations: Option<u64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_string_size: Option<u64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_array_size: Option<u64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_map_size: Option<u64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_call_levels: Option<u64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_expr_depth: Option<u64>,
}

impl ScriptSandbox {
    #[must_use]
    pub const fn empty() -> Self {
        Self {
            max_operations: None,
            max_string_size: None,
            max_array_size: None,
            max_map_size: None,
            max_call_levels: None,
            max_expr_depth: None,
        }
    }

    /// Returns true if every field is `None` (no overrides).
    #[must_use]
    pub const fn is_empty(&self) -> bool {
        self.max_operations.is_none()
            && self.max_string_size.is_none()
            && self.max_array_size.is_none()
            && self.max_map_size.is_none()
            && self.max_call_levels.is_none()
            && self.max_expr_depth.is_none()
    }
}
