//! WASM1 Phase 3c-compose — wire shapes for wasm-side compose dispatch.
//!
//! When a host registers a wasm-backed compose closure in one of the
//! four registries, the closure serialises its native arguments
//! (e.g. `CreationHistory` + `ComposeCtx` accumulator) into one of
//! these arg structs, calls the plugin's `__maat_compose` export with
//! the postcard payload + the recorded function-table index, and
//! deserialises the response into one of the `Result` structs.
//!
//! The structs are co-located in `maat-sdk-types` (not engine or
//! runtime/server) so the plugin-side macro emission can postcard
//! decode them without a host-only dependency.
//!
//! Sub-commit 3 (this file) defines the shapes. Macro emission for
//! `__maat_compose` that actually consumes them is the deferred
//! `WASM1-3c-compose-macro` follow-up.

use crate::creation::CreationHistory;
use crate::events::GameEvent;
use crate::staged_decision::ResolvedGroup;
use crate::types::{EntityId, EntityTypeId, SeatId};

/// Kind of compose dispatch. The host packs this as the first arg to
/// `__maat_compose` so a single plugin export can route to any of the
/// four registered compose flavours.
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum ComposeKind {
    Creation = 0,
    LevelUp = 1,
    Wizard = 2,
    DistanceMetric = 3,
}

/// Wire shape for a `register_creation_spec` compose dispatch.
///
/// The host serialises `(history, accumulator-events-so-far,
/// by_seat, type_id)` into postcard. The plugin's macro-generated
/// `__maat_compose` decodes, runs the registered compose, and returns
/// `CreationComposeResult` with the emitted events + the
/// post-compose accumulator delta.
#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct CreationComposeArgs {
    pub type_id: EntityTypeId,
    pub history: CreationHistory,
    pub by_seat: SeatId,
    /// `ComposeCtx`'s accumulator state — the events already
    /// proposed before this compose runs. Today's `ComposeCtx`
    /// doesn't expose a snapshot accessor; sub-commit 3.2 will add
    /// one if the macro work proceeds.
    pub ctx_proposed: Vec<GameEvent>,
}

/// Plugin-side response to a creation compose call.
#[derive(Debug, Clone, Default)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct CreationComposeResult {
    pub events: Vec<GameEvent>,
    /// Delta to apply to the host-side `ComposeCtx` accumulator.
    pub ctx_proposed: Vec<GameEvent>,
}

/// Wire shape for `register_level_up_spec` compose dispatch.
#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct LevelUpComposeArgs {
    pub type_id: EntityTypeId,
    pub target_level: u32,
    pub history: CreationHistory,
    pub by_seat: SeatId,
    pub entity: EntityId,
    pub ctx_proposed: Vec<GameEvent>,
}

#[derive(Debug, Clone, Default)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct LevelUpComposeResult {
    pub events: Vec<GameEvent>,
    pub ctx_proposed: Vec<GameEvent>,
}

/// Wire shape for `register_wizard_spec` compose dispatch.
#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct WizardComposeArgs {
    pub trigger: GameEvent,
    pub history: Vec<ResolvedGroup>,
    pub by_seat: SeatId,
    pub ctx_proposed: Vec<GameEvent>,
}

/// Plugin-side response to a wizard compose call. Mirrors
/// `composition_registry::ComposeResult` but serialisable.
///
/// `pipeline_result` is a postcard-friendly mirror of
/// `Option<handler::Decision>`: `None` = side-effect-only; `Some` =
/// the wizard's trigger event is allowed / denied accordingly.
#[derive(Debug, Clone, Default)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct WizardComposeResult {
    pub effects: Vec<GameEvent>,
    pub pipeline_result: Option<WirePipelineResult>,
}

/// Wire-friendly mirror of `handler::Decision` minus the `Wizard`
/// variant (which would be a recursive pipeline-result and isn't
/// allowed as a compose-emitted decision today).
#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum WirePipelineResult {
    Allow,
    Deny { reason: String },
}

/// Wire shape for `register_distance_metric` dispatch. The metric
/// receives the two endpoints + the graph topology in summary form
/// (a full BoardGraph snapshot is too expensive to ship per call;
/// macro emission may choose a leaner representation when it lands).
///
/// Sub-commit 3 leaves the args minimal (just the two endpoints
/// as raw FaceId u64); the macro work decides whether the graph
/// itself needs to ride along or whether `GameContext` plumbing is
/// sufficient.
#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct DistanceComposeArgs {
    pub from_face: u64,
    pub to_face: u64,
}

#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct DistanceComposeResult {
    /// `Ok(distance)` or `Err(string)` (postcard-friendly). The
    /// host-side closure remaps `Err` to
    /// `DistanceError::ComputationFailed`.
    pub distance: Result<u32, String>,
}
