//! Generated types from spec/shield-v1.json — DO NOT EDIT.
//!
//! Regenerate: python codegen/generate.py --spec spec/shield-v1.json

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// =============================================================================
// Enums
// =============================================================================

/// Type of content being evaluated.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContentType {
    #[default]
    Prompt,
    Response,
    ToolCall,
    File,
}

/// Guard evaluation decision.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Decision {
    #[default]
    Allow,
    Deny,
}

/// enforce: block on deny. monitor: allow + log. alert: allow + signal.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Mode {
    #[default]
    Enforce,
    Monitor,
    Alert,
}

// =============================================================================
// Structs
// =============================================================================

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AgentIdentityTrace {
    /// Authentication method: api_key or jwt.
    pub auth_method: String,
    /// Identity external identifier.
    pub external_id: String,
    /// Agent framework (e.g. Python SDK, Node SDK).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub framework: Option<String>,
    /// Identity type (agent, service, bot).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub identity_type: Option<String>,
    /// Human-readable identity name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    /// Publisher/organization.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub publisher: Option<String>,
    /// Identity lifecycle status.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,
    /// Type of agent (e.g. claude, cursor, custom).
    pub sub_type: String,
    /// Trust level (verified, unverified, etc.).
    pub trust_level: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EvidenceSource {
    /// Detector name (e.g. injection, secrets, tool_validator).
    pub detector: String,
    /// Producer-specific metadata (e.g. tier, phase, priority).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub labels: Option<HashMap<String, String>>,
    /// Detector execution latency in milliseconds.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub latency_ms: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ConditionResult {
    /// Actual value from the context (null if field was missing).
    pub actual: serde_json::Value,
    /// Expected value (threshold from the rule).
    pub expected: serde_json::Value,
    /// Context field name.
    pub field: String,
    /// Whether this condition matched.
    pub matched: bool,
    /// Comparison operator.
    pub operator: String,
    /// Detector that produced this context value (when available).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source: Option<EvidenceSource>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ContextKeySpec {
    /// Human-readable description.
    pub description: String,
    /// Context key name (e.g. injection_score).
    pub key: String,
    /// Value range (e.g. 0-100).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub range: Option<String>,
    /// Cedar type: long, bool, string, or set.
    #[serde(rename = "type")]
    pub r#type: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EntityDebug {
    /// Entity ID.
    pub id: String,
    /// Parent entity UIDs.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub parents: Option<Vec<String>>,
    /// Entity type.
    #[serde(rename = "type")]
    pub r#type: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TenantDebug {
    /// Account ID.
    pub account_id: String,
    /// Application ID.
    pub application_id: String,
    /// Project ID.
    pub project_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DebugInfo {
    /// Cedar action.
    pub action: String,
    /// Entity hierarchy.
    pub entities: Vec<EntityDebug>,
    /// Number of loaded policies.
    pub loaded_policy_count: i64,
    /// Cedar namespace.
    pub namespace: String,
    /// Principal entity ID.
    pub principal_id: String,
    /// Principal entity type.
    pub principal_type: String,
    /// Product namespace.
    pub product: String,
    /// Resource entity ID.
    pub resource_id: String,
    /// Resource entity type.
    pub resource_type: String,
    /// Resolved tenant identity.
    pub tenant: TenantDebug,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PolicySummary {
    /// Owning account.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub account_id: Option<String>,
    /// Agent scope (empty = not agent-specific).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub agent_id: Option<String>,
    /// App scope (empty = project-wide).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub application_id: Option<String>,
    /// Policy domain category.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub category: Option<String>,
    /// Full label metadata (source, profile_id, etc.).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub labels: Option<HashMap<String, String>>,
    /// Policy mode: enforce, monitor, or alert.
    pub mode: String,
    /// Database UUID.
    pub policy_id: String,
    /// Human-readable name.
    pub policy_name: String,
    /// Owning project.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub project_id: Option<String>,
    /// Cedar @id annotations from this policy.
    pub rule_ids: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DebugPoliciesResponse {
    /// Whether the evaluator has loaded policies.
    pub has_policies: bool,
    /// Loaded policies with IDs, names, modes, and Cedar rule IDs.
    pub policies: Vec<PolicySummary>,
    /// Number of distinct DB policies loaded.
    pub policy_count: i64,
    /// Product namespace inspected.
    pub product: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FileContext {
    /// File extension: pdf, docx, xlsx, etc.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub file_extension: Option<String>,
    /// Original file name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub file_name: Option<String>,
    /// Whether file is encrypted via MIP protection.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub is_encrypted: Option<bool>,
    /// Whether file has IRM/RMS restrictions.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub is_rights_managed: Option<bool>,
    /// File MIME type.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mime_type: Option<String>,
    /// Microsoft Information Protection label GUID.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mip_label_id: Option<String>,
    /// MIP label display name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mip_label_name: Option<String>,
    /// File operation: read, write, delete, append, etc.
    pub operation: String,
    /// File path.
    pub path: String,
    /// Normalized MIP sensitivity: public, internal, confidential, restricted.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sensitivity_level: Option<String>,
    /// File size in bytes.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub size: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MCPContext {
    /// Advertised MCP capabilities from server manifest.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub capabilities: Option<Vec<String>>,
    /// MCP server name.
    pub server_name: String,
    /// MCP server URL.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub server_url: Option<String>,
    /// Transport protocol: sse, stdio, http.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub transport: Option<String>,
    /// Whether the server passed trust/signature verification.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub verified: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ModelContext {
    /// Max token limit for the session.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_tokens: Option<i64>,
    /// Model name (e.g. gpt-4, claude-3).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    /// LLM provider (e.g. openai, anthropic).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider: Option<String>,
    /// Temperature setting.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub temperature: Option<f64>,
    /// Tokens consumed so far in the session.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens_used: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ToolContext {
    /// Tool arguments as a JSON object.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub arguments: Option<HashMap<String, serde_json::Value>>,
    /// Tool description from MCP manifest, for tool-poisoning analysis.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// Whether the tool is built-in to the LLM or externally registered.
    pub is_builtin: bool,
    /// Tool name.
    pub name: String,
    /// MCP server ID (for externally-registered tools).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub server_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DetectRequest {
    /// Content to analyze
    pub content: String,
    /// Type of content being analyzed
    pub content_type: ContentType,
    /// Reference material for context-aware detection (hallucination, groundedness). For response evaluation, include the original prompt and any RAG context.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub contexts: Option<Vec<String>>,
    /// Specific detectors to run. Empty runs all enabled detectors.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub detectors: Option<Vec<String>>,
    /// File operation context
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub file: Option<FileContext>,
    /// MCP server context
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mcp: Option<MCPContext>,
    /// Caller-provided metadata passed through to detectors
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub metadata: Option<HashMap<String, serde_json::Value>>,
    /// LLM model context
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub model: Option<ModelContext>,
    /// Session ID for cross-turn state tracking
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub session_id: Option<String>,
    /// Tool call context
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tool: Option<ToolContext>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DetectorResult {
    /// Emitted context attributes (e.g. injection_score).
    pub context: HashMap<String, serde_json::Value>,
    /// Error message (when status is error or timeout).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    /// Detector execution latency in milliseconds.
    pub latency_ms: i64,
    /// Detector name (e.g. injection, secrets, toxicity).
    pub name: String,
    /// Detector status: healthy, degraded, error, or timeout.
    pub status: String,
    /// Detector tier: fast, standard, or slow.
    pub tier: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DetectResponse {
    /// Merged context from all detectors.
    pub context: HashMap<String, serde_json::Value>,
    /// Per-detector results.
    pub detectors: Vec<DetectorResult>,
    /// Total detection latency in milliseconds.
    pub latency_ms: i64,
    /// Detector tiers that ran.
    pub tiers_evaluated: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DetectorInfo {
    /// Detection category (e.g. semantic, tools, pii).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub category: Option<String>,
    /// Configuration schema type (if configurable).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub config_type: Option<String>,
    /// Whether the detector accepts configuration.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub configurable: Option<bool>,
    /// Keys this detector may emit.
    pub context_keys: Vec<ContextKeySpec>,
    /// What the detector detects.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// Human-readable display name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub display_name: Option<String>,
    /// Typical latency estimate.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub latency: Option<String>,
    /// Detector name.
    pub name: String,
    /// Detector status: healthy, degraded, or disabled.
    pub status: String,
    /// Tags for filtering.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>,
    /// Detector tier: fast, standard, or slow.
    pub tier: String,
    /// Detector version.
    pub version: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DetectorPlanEntry {
    /// Full scoped config (dry run only).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub config: Option<HashMap<String, serde_json::Value>>,
    /// Detector name.
    pub name: String,
    /// Why included/excluded: policy_required, always_run, not_required.
    pub reason: String,
    /// Detector tier.
    pub tier: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DeterminingPolicy {
    /// Custom Cedar annotations.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub annotations: Option<HashMap<String, String>>,
    /// Policy domain (e.g. secrets, pii, injection).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub category: Option<String>,
    /// Cedar effect: permit or forbid.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub effect: Option<String>,
    /// Policy mode: enforce, monitor, or alert.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mode: Option<String>,
    /// Database UUID (for dashboard linking).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub policy_id: Option<String>,
    /// Human-readable policy name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub policy_name: Option<String>,
    /// Cedar @id annotation (e.g. secrets-block-prompts).
    pub rule_id: String,
    /// From Cedar @severity annotation.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub severity: Option<String>,
    /// From Cedar @tags annotation.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EvaluatedExpression {
    /// Actual value from the context.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub actual: Option<serde_json::Value>,
    /// Child expression (for not nodes).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub child: Option<Box<EvaluatedExpression>>,
    /// Child expressions (for and/or nodes).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub children: Option<Box<Vec<EvaluatedExpression>>>,
    /// Expected value (threshold from the rule).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub expected: Option<serde_json::Value>,
    /// Context field name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub field: Option<String>,
    /// Expression kind: comparison, contains, like, has, and, or, not, raw.
    pub kind: String,
    /// Whether this expression matched.
    pub matched: bool,
    /// Comparison operator.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub operator: Option<String>,
    /// Like pattern (for like expressions).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub pattern: Option<String>,
    /// Detector that produced this context value (leaf nodes only).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source: Option<EvidenceSource>,
    /// Raw text (for raw expressions).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PolicyExplanation {
    /// Per-condition match results (flat list from conditions or tree).
    pub condition_results: Vec<ConditionResult>,
    /// Policy effect: permit or forbid.
    pub effect: String,
    /// Recursive evaluated condition tree with actual values.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub evaluated_expression: Option<EvaluatedExpression>,
    /// Policy ID.
    pub policy_id: String,
    /// Raw Cedar condition text (when no structured conditions).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub raw_condition: Option<String>,
    /// Human-readable explanation summary.
    pub summary: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ExplainedDecision {
    /// Decision effect: Allow or Deny.
    pub effect: String,
    /// Enriched explanations for each determining policy.
    pub explanations: Vec<PolicyExplanation>,
    /// Determining policy IDs with no matching rule in the provided rules.
    pub unmatched_policies: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct GuardRequest {
    /// Cedar action to authorize (e.g. process_prompt, call_tool, read_file, write_file, connect_server)
    pub action: String,
    /// Content to evaluate (prompt text, tool call arguments, file content, etc.)
    pub content: String,
    /// Type of content being evaluated
    pub content_type: ContentType,
    /// Reference material for context-aware detection (hallucination, groundedness). For response evaluation, include the original prompt and any RAG context.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub contexts: Option<Vec<String>>,
    /// Include debug-tier fields: detectors[] (per-detector breakdown), context (raw merged output), debug_info (Cedar eval inputs). Implies explain=true.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub debug: Option<bool>,
    /// Specific detectors to run. Empty runs all enabled detectors.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub detectors: Option<Vec<String>>,
    /// When true and optimize is true, return the optimization plan without executing detectors or Cedar evaluation.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub dryrun: Option<bool>,
    /// Enable early exit on deny after each tier (skips slower tiers)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub early_exit: Option<bool>,
    /// Include explain-tier fields: projected_context, eval_latency_ms, tiers_evaluated, tiers_skipped, explanation, root_causes.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub explain: Option<bool>,
    /// File operation context (path, operation, size)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub file: Option<FileContext>,
    /// MCP server interaction context
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mcp: Option<MCPContext>,
    /// Caller-provided metadata passed through to detectors
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub metadata: Option<HashMap<String, serde_json::Value>>,
    /// enforce: block on deny. monitor: allow + log actual_decision. alert: allow + signal alerting pipeline.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mode: Option<Mode>,
    /// LLM model context (provider, temperature, token usage)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub model: Option<ModelContext>,
    /// When true, only run detectors whose outputs are referenced by active policies for this scope.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub optimize: Option<bool>,
    /// Session ID for cross-turn state tracking (enables cumulative risk, action sequences, token budgets)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub session_id: Option<String>,
    /// Tool call context for agentic evaluation
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tool: Option<ToolContext>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct OptimizationReport {
    /// Policies matching this action/product scope.
    pub active_policies: Vec<String>,
    /// True if fell back to running all detectors.
    pub fallback_to_all: bool,
    /// Why this optimization plan was chosen.
    pub reason: String,
    /// Context keys required by active policies.
    pub required_context_keys: Vec<String>,
    /// Detectors required by policies.
    pub required_detectors: Vec<DetectorPlanEntry>,
    /// Detectors skipped (not required by policies).
    pub skipped_detectors: Vec<DetectorPlanEntry>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RootCause {
    /// Detector that triggered.
    pub detector: String,
    /// Supporting evidence.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub evidence: Option<HashMap<String, serde_json::Value>>,
    /// Key-value pairs explaining the threat.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub labels: Option<HashMap<String, String>>,
    /// Human-readable summary of the root cause.
    pub summary: String,
    /// Policy IDs triggered by this root cause.
    pub triggered_policies: Vec<String>,
    /// Context values that caused the trigger.
    pub triggering_context: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SessionDelta {
    /// Updated cumulative risk score (0-100).
    pub cumulative_risk: f64,
    /// Action performed in this turn.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub new_action: Option<String>,
    /// Tokens used in this turn.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens_used_delta: Option<i64>,
    /// Updated turn count.
    pub turn_count: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Signal {
    /// Taxonomy domain (e.g. semantic, tools, agent_security).
    pub category: String,
    /// Projected context key that triggered this signal.
    pub context_key: String,
    /// Human-readable signal name from taxonomy.
    pub name: String,
    /// Normalized detection score (0-100). Boolean detectors default to 100.
    pub score: i64,
    /// Severity from taxonomy: low, medium, high, or critical.
    pub severity: String,
    /// Taxonomy vulnerability ID (e.g. prompt_injection, credential_leakage).
    pub vulnerability_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct GuardResponse {
    /// Cedar decision before mode override.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub actual_decision: Option<String>,
    /// Authenticated agent identity (when agent request).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub agent_identity: Option<AgentIdentityTrace>,
    /// True when alert-mode policy fired.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub alerted: Option<bool>,
    /// Merged detector context (when debug=true).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub context: Option<HashMap<String, serde_json::Value>>,
    /// Cedar evaluation debug info (when debug=true).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub debug_info: Option<DebugInfo>,
    /// Guard decision: allow or deny.
    pub decision: Decision,
    /// Per-detector results (when debug=true).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub detectors: Option<Vec<DetectorResult>>,
    /// Policies that determined the decision.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub determining_policies: Option<Vec<DeterminingPolicy>>,
    /// Strictest mode among determining policies.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub effective_mode: Option<String>,
    /// Cedar diagnostic errors (rare).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub eval_errors: Option<String>,
    /// Cedar evaluation latency in milliseconds.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub eval_latency_ms: Option<i64>,
    /// Structured policy explanation (when explain=true).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub explanation: Option<ExplainedDecision>,
    /// Total evaluation latency in milliseconds.
    pub latency_ms: i64,
    /// True if decision was changed by per-policy mode.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mode_overridden: Option<bool>,
    /// Mode override explanation (monitor/alert).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub mode_reason: Option<String>,
    /// Detector optimization plan (when optimize=true).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub optimization: Option<OptimizationReport>,
    /// Human-readable policy decision reasoning.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub policy_reason: Option<String>,
    /// Cedar-normalized context sent to the evaluator (when explain=true).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub projected_context: Option<HashMap<String, serde_json::Value>>,
    /// Request trace ID.
    pub request_id: String,
    /// Root cause analysis for triggered detections.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub root_causes: Option<Vec<RootCause>>,
    /// Session state changes after evaluation.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub session_delta: Option<SessionDelta>,
    /// Taxonomy-aligned detection signals. Always populated when detectors fire above threshold, sorted by severity.
    pub signals: Vec<Signal>,
    /// Detector tiers that ran (fast, standard, slow).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tiers_evaluated: Option<Vec<String>>,
    /// Detector tiers skipped due to early exit.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tiers_skipped: Option<Vec<String>>,
    /// Response timestamp (RFC 3339).
    pub timestamp: String,
}

impl GuardResponse {
    /// True if the guard decision is 'allow'.
    pub fn allowed(&self) -> bool {
        self.decision == Decision::Allow
    }

    /// True if the guard decision is 'deny'.
    pub fn denied(&self) -> bool {
        self.decision == Decision::Deny
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct HealthResponse {
    /// Per-detector health status (healthy, degraded, or error)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub detectors: Option<HashMap<String, String>>,
    /// Cedar evaluator status
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub evaluator: Option<String>,
    /// Overall health status
    pub status: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ListDetectorsResponse {
    /// Total number of registered detectors
    pub count: i64,
    /// Available detectors with name, version, tier, context keys, and health status
    pub detectors: Vec<DetectorInfo>,
}

/// RFC 9457 Problem Details error format.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ProblemDetails {
    /// Detailed error message.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub detail: Option<String>,
    /// HTTP status code.
    pub status: i64,
    /// Short error title.
    pub title: String,
}

/// A Server-Sent Event from the guard stream.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StreamEvent {
    /// Event payload (DetectorResult for detector_result, GuardResponse for decision).
    pub data: HashMap<String, serde_json::Value>,
    /// Event type: detector_result (per-detector), decision (final), or error.
    #[serde(rename = "type")]
    pub r#type: String,
}

/// Response from the token exchange endpoint (used by SDK auth).
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TokenResponse {
    /// RS256 JWT token.
    pub access_token: String,
    /// Account ID from token claims.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub account_id: Option<String>,
    /// Token lifetime in seconds.
    pub expires_in: i64,
    /// Gateway ID from token claims.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub gateway_id: Option<String>,
    /// Project ID from token claims.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub project_id: Option<String>,
    /// Token type (always Bearer).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub token_type: Option<String>,
}
