//! Interactive worlds — `extensions/interactive-worlds.md`.
//!
//! Media that consumes input. A world is a deterministic, content-
//! addressed artifact; everything that ever happens in it is a signed
//! operation ([`op::CausalOp`]); and any op set replays to a bit-exact
//! state hash on every conformant sink (**replay-equivalence**, the
//! temporal generalization of the core spec's decode-equivalence).
//!
//! The single-player input log and the multiplayer op log are the same
//! object: an input event is an op emitted by one actor. One actor's
//! log is a session, a sealed log is a replay, N interleaved logs are
//! a shared world. Three capabilities, one container format
//! ([`container::WorldContainer`], magic `WWLD`), differing only in
//! the op log's seal state.
//!
//! What this module standardizes: the container, the op, canonical
//! ordering, causality, the simulation contract, state hashing, and
//! convergence. What it deliberately does NOT standardize: a physics
//! engine. Sim capabilities register separately ([`sim::Sim`]) under
//! the contract that they be deterministic under the declared numeric
//! profile, pure per tick, and convergent. [`sim::Kinematic0`] is the
//! reference registrant the conformance corpus is built on.

pub mod container;
pub mod contract;
pub mod feed;
pub mod fixed;
pub mod graph;
pub mod op;
pub mod receipt;
pub mod replay;
pub mod rng;
pub mod sim;
pub mod skeletal;
pub mod truss;

pub use container::{AssetEntry, Seal, WorldContainer, MAGIC_WWLD};
pub use contract::SimContract;
pub use fixed::Fx;
pub use graph::WorldState;
pub use op::{mint_node_id, CausalOp, OpId};
pub use receipt::SessionReceipt;
pub use replay::{
    collect_checkpoints, collect_checkpoints_and_work, collect_checkpoints_sampled,
    run_sampled_dispatch, run_sealed_dispatch, Checkpoint, LiveWorld, Replayer,
};
pub use rng::Xoshiro256pp;
pub use feed::{Cell, FeedRecord, FeedState, CAP_SIM_FEED0};
pub use skeletal::{Bone, Skeletal0, CAP_SIM_SKELETAL0};
pub use sim::{Ballistic0, Kinematic0, Resolve, Sim};
pub use truss::{Truss0, TrussNode};

/// World identity (extension §7.4 / §8): BLAKE3 over the scene — the
/// canonical contract bytes and tick-0 graph bytes — domain
/// `wai:world-scene\x01`. Two sessions of the same world, and two
/// actors who "opened the same hash", share this value.
pub fn world_hash(contract_bytes: &[u8], graph_bytes: &[u8]) -> [u8; 32] {
    let mut h = blake3::Hasher::new();
    h.update(DOMAIN_SCENE);
    h.update(contract_bytes);
    h.update(graph_bytes);
    *h.finalize().as_bytes()
}

/// Content hash of an asset (extension §4.1): `BLAKE3(DOMAIN_ASSET ||
/// envelope_bytes)`, computed over a **complete WAI envelope** (the
/// `WAI1` magic + manifest + payload), the same discipline as
/// jwp-receipts §2.1. An [`AssetEntry`]'s `hash` is this value; a sink
/// fetches the envelope, recomputes the hash, and rejects a mismatch
/// before decoding. Assets are presentation-only — neither this hash
/// nor the asset bytes ever enter the state hash, which is what keeps
/// conformance state-level while presentation streams independently.
pub fn asset_content_hash(envelope_bytes: &[u8]) -> [u8; 32] {
    let mut h = blake3::Hasher::new();
    h.update(DOMAIN_ASSET);
    h.update(envelope_bytes);
    *h.finalize().as_bytes()
}

/// Registered capability strings live in the central registry
/// ([`crate::codecs`], SPEC.md §5 registration discipline: once
/// registered, the payload format is fixed forever); re-exported here
/// for locality.
pub use crate::codecs::{
    CAP_MESH_GLB, CAP_SPLAT_SPZ, CAP_WORLD_LIVE, CAP_WORLD_REPLAY, CAP_WORLD_SCENE,
};

/// Registered deterministic numeric profiles (extension §6.2).
pub const NUMERIC_FIXED64: &str = "wai.det.fixed64";

/// Registered conflict-resolution functions (extension §6.5).
pub const RESOLVE_LWW: &str = "wai.world.resolve.lww";
pub const RESOLVE_SUM: &str = "wai.world.resolve.sum";

/// Domain separators. Same discipline as jwp-receipts §2.1: every hash
/// and signature in this extension is domain-separated so bytes can
/// never be replayed across contexts.
pub const DOMAIN_OP: &[u8] = b"wai:world-op\x01";
pub const DOMAIN_NODE: &[u8] = b"wai:world-node\x01";
pub const DOMAIN_STATE: &[u8] = b"wai:world-state\x01";
pub const DOMAIN_SEAL: &[u8] = b"wai:world-seal\x01";
pub const DOMAIN_OPS_BATCH: &[u8] = b"wai:world-ops\x01";
pub const DOMAIN_SCENE: &[u8] = b"wai:world-scene\x01";
pub const DOMAIN_ASSET: &[u8] = b"wai:asset\x01";

/// Errors across the world module. Conformance is all-or-nothing
/// (extension §7.2): a file that fails MUST be reported failed, never
/// presented as approximately right — hence no lossy/lenient variants.
#[derive(Debug)]
pub enum WorldError {
    /// First 4 payload bytes are not `WWLD`.
    BadMagic,
    /// Byte stream ended mid-structure.
    Truncated,
    /// A required section is absent (kind in payload).
    MissingSection(u8),
    /// An Ed25519 signature failed verification.
    BadSignature,
    /// Op's actor bytes are not a valid Ed25519 public key.
    BadActorKey,
    /// A dep names an op not present in the log / not yet applied.
    DepMissing(OpId),
    /// Sealed log is not in canonical `(effect_tick, lamport, actor)` order.
    NotCanonicalOrder,
    /// Duplicate op id in a log.
    DuplicateOp(OpId),
    /// `effect_tick == 0`: tick 0 is the authored graph, ops start at 1.
    EffectTickZero,
    /// Op lands at or before a receipted (sealed) checkpoint — history
    /// is immutable (extension §5.6).
    SealedHistory { effect_tick: u64, sealed_tick: u64 },
    /// Mutation kind the declared sim capability does not register.
    UnknownMutationKind(u16),
    /// Mutation payload malformed for its kind.
    BadMutation,
    /// Create-op target does not equal `mint_node_id(actor, lamport)`
    /// (extension §5.4 — minting is deterministic, never chosen).
    BadNodeMint,
    /// Computed state hash differs from the seal / expectation.
    HashMismatch { tick: u64, expected: [u8; 32], got: [u8; 32] },
    /// Seal section present on a non-replay, absent on a replay, or
    /// its final tick disagrees with the op log.
    BadSeal(&'static str),
    /// Simulation contract JSON is malformed or names an unregistered
    /// profile / algorithm.
    BadContract(String),
    /// The contract's `sim` names a capability this sink does not have
    /// — the world is inert here (SPEC.md §4 step 6 semantics).
    UnknownSimCapability(String),
}

impl std::fmt::Display for WorldError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::BadMagic => write!(f, "payload is not a WWLD container"),
            Self::Truncated => write!(f, "byte stream truncated"),
            Self::MissingSection(k) => write!(f, "required section 0x{k:02x} missing"),
            Self::BadSignature => write!(f, "Ed25519 signature verification failed"),
            Self::BadActorKey => write!(f, "actor bytes are not a valid Ed25519 public key"),
            Self::DepMissing(id) => write!(f, "dep not satisfied: lamport {} of actor {}", id.lamport, hex8(&id.actor)),
            Self::NotCanonicalOrder => write!(f, "sealed op log not in canonical order"),
            Self::DuplicateOp(id) => write!(f, "duplicate op: lamport {} of actor {}", id.lamport, hex8(&id.actor)),
            Self::EffectTickZero => write!(f, "effect_tick 0 is reserved for the authored graph"),
            Self::SealedHistory { effect_tick, sealed_tick } => write!(
                f, "op at tick {effect_tick} lands at/before receipted checkpoint {sealed_tick}: history is immutable"
            ),
            Self::UnknownMutationKind(k) => write!(f, "mutation kind 0x{k:04x} not registered by the sim capability"),
            Self::BadMutation => write!(f, "mutation payload malformed for its kind"),
            Self::BadNodeMint => write!(f, "create target != BLAKE3-minted node id"),
            Self::HashMismatch { tick, .. } => write!(f, "state hash mismatch at tick {tick}"),
            Self::BadSeal(why) => write!(f, "bad seal: {why}"),
            Self::BadContract(why) => write!(f, "bad simulation contract: {why}"),
            Self::UnknownSimCapability(cap) => write!(f, "missing sim capability `{cap}` — world is inert at this sink"),
        }
    }
}

impl std::error::Error for WorldError {}

fn hex8(bytes: &[u8; 32]) -> String {
    bytes[..4].iter().map(|b| format!("{b:02x}")).collect()
}
