use car_a2ui::PatchComponents;
use serde::{Deserialize, Serialize};
use serde_json::Value;

/// Stable structural fingerprint of an A2UI surface — the strategy
/// library's key. Mirrors the Swift `IntentSignature` shape in
/// `a2ui-shader/Sources/A2UIShaderCore/Telemetry.swift`. Wire format
/// matches the renderer's serde rename so reports decode cleanly off
/// the `RenderReport.signature` `Value`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct IntentSignature {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub domain: Option<String>,
    pub structure: String,
    pub cardinality_band: String,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub feature_set: Vec<String>,
    pub action_count_band: String,

    // v2 axes — all optional, all forward-compatible.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub temporal: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub attention_budget: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub criticality: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub hierarchy_depth: Option<u32>,
}

impl IntentSignature {
    /// Decode from a renderer's `RenderReport.signature` `Value`.
    /// Tolerates missing fields by falling back to schema defaults.
    pub fn from_value(value: &Value) -> Result<Self, serde_json::Error> {
        serde_json::from_value(value.clone())
    }
}

/// Outcome of a single `UIImprovementAgent::consume` call.
///
/// `Patch` carries the strategy id, a fingerprint of the patch ops
/// (`patch_hash`), and the elapsed time of the decision. The
/// fingerprint lets a runtime-side convergence monitor detect
/// oscillation (A→B→A patch loops across renders) without re-hashing.
/// `elapsed_ns` is for observability budgets; if it ever exceeds
/// a few ms in p95 we move the strategy library off the hot path.
#[derive(Debug, Clone, PartialEq)]
pub enum Decision {
    Patch {
        envelope: PatchComponents,
        strategy_id: String,
        patch_hash: u64,
        elapsed_ns: u128,
    },
    /// No proposal — signature stable for the configured window, or
    /// no strategy had an opinion, or the agent already proposed at
    /// this report.
    StableNoChange,
    /// Loud failure — the renderer painted unknown components
    /// (`placeholder_count > 0`). Caller decides whether to retry;
    /// the improvement loop MUST pause for this surface.
    HardStop { reason: String },
}
