//! Host-side metadata types for package type information.
//!
//! Packages embed CGRF-encoded metadata accessible via `__pack_types`.
//! This module provides types and a decoder for that metadata.
//!
//! Uses the unified type system from `crate::types`.
//!
//! ## Interface Hashes (Merkle Tree)
//!
//! Each interface has a content-addressed hash computed from its structure:
//! - Types are hashed structurally (field names included, type names excluded)
//! - Functions are hashed by their signature (param types + result types)
//! - Interfaces include bindings (name → hash pairs)
//!
//! This enables O(1) compatibility checking: if hashes match, interfaces are compatible.

use crate::abi::{decode, encode, Value};
use crate::types::{Arena, Case, Field, Function, Param, Type, TypeDef, TypePath};

// ============================================================================
// Interface Hashes
// ============================================================================

/// A 256-bit type hash for Merkle tree-based type compatibility.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypeHash([u8; 32]);

impl TypeHash {
    /// Create from raw bytes.
    pub fn from_bytes(bytes: [u8; 32]) -> Self {
        Self(bytes)
    }

    /// Get the raw bytes.
    pub fn as_bytes(&self) -> &[u8; 32] {
        &self.0
    }

    /// Create from a tuple of 4 u64s (WASM representation).
    pub fn from_u64s(a: u64, b: u64, c: u64, d: u64) -> Self {
        let mut bytes = [0u8; 32];
        bytes[0..8].copy_from_slice(&a.to_le_bytes());
        bytes[8..16].copy_from_slice(&b.to_le_bytes());
        bytes[16..24].copy_from_slice(&c.to_le_bytes());
        bytes[24..32].copy_from_slice(&d.to_le_bytes());
        Self(bytes)
    }

    /// Convert to a tuple of 4 u64s.
    pub fn to_u64s(&self) -> (u64, u64, u64, u64) {
        let b = &self.0;
        (
            u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
            u64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]),
            u64::from_le_bytes([b[16], b[17], b[18], b[19], b[20], b[21], b[22], b[23]]),
            u64::from_le_bytes([b[24], b[25], b[26], b[27], b[28], b[29], b[30], b[31]]),
        )
    }

    /// Format as hex string (for display).
    pub fn to_hex(&self) -> String {
        use core::fmt::Write;
        let mut s = String::with_capacity(self.0.len() * 2);
        for b in &self.0 {
            let _ = write!(s, "{:02x}", b);
        }
        s
    }

    /// Format as short hex (first 8 chars).
    pub fn to_short_hex(&self) -> String {
        use core::fmt::Write;
        let mut s = String::with_capacity(8);
        for b in self.0.iter().take(4) {
            let _ = write!(s, "{:02x}", b);
        }
        s
    }

    /// Const function to create from bytes (for compile-time constants).
    pub const fn from_bytes_const(bytes: [u8; 32]) -> Self {
        Self(bytes)
    }
}

/// Placeholder for self-references and dynamic value types.
///
/// Used for:
/// - Recursive types where a type references itself
/// - The `value` type which represents any Pack value dynamically
///
/// This matches the HASH_SELF_REF constant in pack-abi for consistency.
pub const HASH_SELF_REF: TypeHash = TypeHash::from_bytes_const([
    0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

impl std::fmt::Display for TypeHash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_short_hex())
    }
}

// ============================================================================
// Primitive Type Hash Constants
// ============================================================================

/// Hash for the `bool` type.
pub const HASH_BOOL: TypeHash = TypeHash([
    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `u8` type.
pub const HASH_U8: TypeHash = TypeHash([
    0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `u16` type.
pub const HASH_U16: TypeHash = TypeHash([
    0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `u32` type.
pub const HASH_U32: TypeHash = TypeHash([
    0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `u64` type.
pub const HASH_U64: TypeHash = TypeHash([
    0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `s8` type.
pub const HASH_S8: TypeHash = TypeHash([
    0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `s16` type.
pub const HASH_S16: TypeHash = TypeHash([
    0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `s32` type.
pub const HASH_S32: TypeHash = TypeHash([
    0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `s64` type.
pub const HASH_S64: TypeHash = TypeHash([
    0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `f32` type.
pub const HASH_F32: TypeHash = TypeHash([
    0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `f64` type.
pub const HASH_F64: TypeHash = TypeHash([
    0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `char` type.
pub const HASH_CHAR: TypeHash = TypeHash([
    0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `string` type.
pub const HASH_STRING: TypeHash = TypeHash([
    0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

/// Hash for the `flags` type.
pub const HASH_FLAGS: TypeHash = TypeHash([
    0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);

// ============================================================================
// Hash Builder and Functions
// ============================================================================

use sha2::{Digest, Sha256};

// Hash construction tags (different from CGRF wire format tags)
const HASH_TAG_LIST: u8 = 0x10;
const HASH_TAG_OPTION: u8 = 0x11;
const HASH_TAG_RESULT: u8 = 0x12;
const HASH_TAG_TUPLE: u8 = 0x13;
const HASH_TAG_RECORD: u8 = 0x14;
const HASH_TAG_VARIANT: u8 = 0x15;
const HASH_TAG_FUNCTION: u8 = 0x16;
const HASH_TAG_INTERFACE: u8 = 0x17;

/// Builder for computing type hashes.
struct TypeHasher {
    hasher: Sha256,
}

impl TypeHasher {
    fn new() -> Self {
        Self {
            hasher: Sha256::new(),
        }
    }

    fn tag(mut self, tag: u8) -> Self {
        self.hasher.update([tag]);
        self
    }

    fn string(mut self, s: &str) -> Self {
        self.hasher.update((s.len() as u32).to_le_bytes());
        self.hasher.update(s.as_bytes());
        self
    }

    fn child(mut self, hash: &TypeHash) -> Self {
        self.hasher.update(hash.as_bytes());
        self
    }

    fn count(mut self, n: usize) -> Self {
        self.hasher.update((n as u32).to_le_bytes());
        self
    }

    fn finish(self) -> TypeHash {
        let result = self.hasher.finalize();
        let mut bytes = [0u8; 32];
        bytes.copy_from_slice(&result);
        TypeHash(bytes)
    }
}

/// Hash a list type.
pub fn hash_list(element: &TypeHash) -> TypeHash {
    TypeHasher::new().tag(HASH_TAG_LIST).child(element).finish()
}

/// Hash an option type.
pub fn hash_option(inner: &TypeHash) -> TypeHash {
    TypeHasher::new().tag(HASH_TAG_OPTION).child(inner).finish()
}

/// Hash a result type.
pub fn hash_result(ok: &TypeHash, err: &TypeHash) -> TypeHash {
    TypeHasher::new()
        .tag(HASH_TAG_RESULT)
        .child(ok)
        .child(err)
        .finish()
}

/// Hash a tuple type.
pub fn hash_tuple(elements: &[TypeHash]) -> TypeHash {
    let mut hasher = TypeHasher::new().tag(HASH_TAG_TUPLE).count(elements.len());
    for elem in elements {
        hasher = hasher.child(elem);
    }
    hasher.finish()
}

/// Hash a record type (structural - name NOT included).
pub fn hash_record(fields: &[(&str, TypeHash)]) -> TypeHash {
    let mut hasher = TypeHasher::new().tag(HASH_TAG_RECORD).count(fields.len());
    for (name, type_hash) in fields {
        hasher = hasher.string(name).child(type_hash);
    }
    hasher.finish()
}

/// Hash a variant type (structural - name NOT included).
pub fn hash_variant(cases: &[(&str, Option<TypeHash>)]) -> TypeHash {
    let mut hasher = TypeHasher::new().tag(HASH_TAG_VARIANT).count(cases.len());
    for (name, payload) in cases {
        hasher = hasher.string(name);
        if let Some(type_hash) = payload {
            hasher = hasher.tag(1).child(type_hash);
        } else {
            hasher = hasher.tag(0);
        }
    }
    hasher.finish()
}

/// Hash a function signature.
pub fn hash_function(params: &[TypeHash], results: &[TypeHash]) -> TypeHash {
    let mut hasher = TypeHasher::new().tag(HASH_TAG_FUNCTION).count(params.len());
    for param in params {
        hasher = hasher.child(param);
    }
    hasher = hasher.count(results.len());
    for result in results {
        hasher = hasher.child(result);
    }
    hasher.finish()
}

/// An interface binding: name -> hash.
pub struct Binding<'a> {
    pub name: &'a str,
    pub hash: TypeHash,
}

/// Hash an interface (includes binding names).
pub fn hash_interface(
    name: &str,
    type_bindings: &[Binding<'_>],
    func_bindings: &[Binding<'_>],
) -> TypeHash {
    let mut hasher = TypeHasher::new()
        .tag(HASH_TAG_INTERFACE)
        .string(name)
        .count(type_bindings.len());

    for binding in type_bindings {
        hasher = hasher.string(binding.name).child(&binding.hash);
    }

    hasher = hasher.count(func_bindings.len());
    for binding in func_bindings {
        hasher = hasher.string(binding.name).child(&binding.hash);
    }

    hasher.finish()
}

/// An interface with its Merkle-tree hash.
#[derive(Debug, Clone)]
pub struct InterfaceHash {
    /// Interface name (e.g., "theater:simple/runtime").
    pub name: String,
    /// Content-addressed hash of the interface structure.
    pub hash: TypeHash,
}

// ============================================================================
// Type Hashing from Arena Types
// ============================================================================

/// Compute the TypeHash for a Type from the types module.
///
/// Named references (`Type::Ref`) are resolved structurally when this is called
/// via the in-scope variants. This bare entry point has no resolution context,
/// so it falls back to a path-based hash for refs — useful only for primitive
/// or fully-monomorphic types.
pub fn hash_type(ty: &Type) -> TypeHash {
    hash_type_in(ty, &[])
}

/// Compute the TypeHash for a Type, resolving named refs against `types`.
///
/// When a `Type::Ref` names a local typedef, the underlying record/variant/etc
/// is hashed structurally — matching the actor-side `pack_types!` macro which
/// inlines refs at metadata-emission time. Self-references and cycles return
/// `HASH_SELF_REF`.
pub fn hash_type_in(ty: &Type, types: &[TypeDef]) -> TypeHash {
    hash_type_inner(ty, types, &mut Vec::new())
}

fn hash_type_inner(ty: &Type, types: &[TypeDef], stack: &mut Vec<String>) -> TypeHash {
    match ty {
        Type::Unit => hash_tuple(&[]), // Unit is empty tuple
        Type::Bool => HASH_BOOL,
        Type::U8 => HASH_U8,
        Type::U16 => HASH_U16,
        Type::U32 => HASH_U32,
        Type::U64 => HASH_U64,
        Type::S8 => HASH_S8,
        Type::S16 => HASH_S16,
        Type::S32 => HASH_S32,
        Type::S64 => HASH_S64,
        Type::F32 => HASH_F32,
        Type::F64 => HASH_F64,
        Type::Char => HASH_CHAR,
        Type::String => HASH_STRING,
        Type::List(inner) => hash_list(&hash_type_inner(inner, types, stack)),
        Type::Option(inner) => hash_option(&hash_type_inner(inner, types, stack)),
        Type::Result { ok, err } => hash_result(
            &hash_type_inner(ok, types, stack),
            &hash_type_inner(err, types, stack),
        ),
        Type::Tuple(elems) => {
            let hashes: Vec<_> = elems
                .iter()
                .map(|t| hash_type_inner(t, types, stack))
                .collect();
            hash_tuple(&hashes)
        }
        Type::Ref(path) => hash_ref(path, types, stack),
        Type::Value => HASH_SELF_REF,
    }
}

fn hash_ref(path: &TypePath, types: &[TypeDef], stack: &mut Vec<String>) -> TypeHash {
    // Explicit self-reference: `self` in a recursive type definition.
    if path.is_self_ref() {
        return HASH_SELF_REF;
    }

    // Simple named ref: try to resolve against the in-scope typedefs.
    if let Some(name) = path.as_simple() {
        // Cycle: this name is already being hashed further up the stack.
        if stack.iter().any(|s| s == name) {
            return HASH_SELF_REF;
        }
        if let Some(td) = types.iter().find(|t| t.name() == name) {
            stack.push(name.to_string());
            let h = hash_typedef_inner(td, types, stack);
            stack.pop();
            return h;
        }
    }

    // Unresolved or qualified path: fall back to a path-based hash so refs
    // to types we can't see still produce a stable (if nominal) hash.
    let path_str = path.to_string();
    let mut hasher = sha2::Sha256::new();
    hasher.update(b"ref:");
    hasher.update(path_str.as_bytes());
    TypeHash::from_bytes(hasher.finalize().into())
}

fn hash_typedef_inner(td: &TypeDef, types: &[TypeDef], stack: &mut Vec<String>) -> TypeHash {
    match td {
        TypeDef::Alias { ty, .. } => hash_type_inner(ty, types, stack),
        TypeDef::Record { fields, .. } => {
            // Collect (name, hash) pairs, sort by name for canonical ordering.
            let pairs: Vec<(String, TypeHash)> = fields
                .iter()
                .map(|f| (f.name.clone(), hash_type_inner(&f.ty, types, stack)))
                .collect();
            let mut sorted: Vec<_> = pairs.iter().map(|(n, h)| (n.as_str(), *h)).collect();
            sorted.sort_by(|a, b| a.0.cmp(b.0));
            hash_record(&sorted)
        }
        TypeDef::Variant { cases, .. } => {
            // Unit payloads → None (matches actor-side `Option<TypeDesc>` shape).
            let pairs: Vec<(String, Option<TypeHash>)> = cases
                .iter()
                .map(|c| {
                    let payload = if c.payload.is_unit() {
                        None
                    } else {
                        Some(hash_type_inner(&c.payload, types, stack))
                    };
                    (c.name.clone(), payload)
                })
                .collect();
            let mut sorted: Vec<_> = pairs.iter().map(|(n, h)| (n.as_str(), *h)).collect();
            sorted.sort_by(|a, b| a.0.cmp(b.0));
            hash_variant(&sorted)
        }
        TypeDef::Enum { cases, .. } => {
            // Enum hashes as a variant with all-None payloads (matches actor side).
            let mut sorted: Vec<(&str, Option<TypeHash>)> =
                cases.iter().map(|c| (c.as_str(), None)).collect();
            sorted.sort_by(|a, b| a.0.cmp(b.0));
            hash_variant(&sorted)
        }
        TypeDef::Flags { .. } => HASH_FLAGS,
    }
}

/// Compute the hash for a function signature with no in-scope typedefs.
///
/// Equivalent to `hash_function_from_sig_in(func, &[])`. Refs resolve nominally.
pub fn hash_function_from_sig(func: &Function) -> TypeHash {
    hash_function_from_sig_in(func, &[])
}

/// Compute the hash for a function signature, resolving refs against `types`.
pub fn hash_function_from_sig_in(func: &Function, types: &[TypeDef]) -> TypeHash {
    let param_hashes: Vec<_> = func
        .params
        .iter()
        .map(|p| hash_type_in(&p.ty, types))
        .collect();
    let result_hashes: Vec<_> = func
        .results
        .iter()
        .map(|t| hash_type_in(t, types))
        .collect();
    hash_function(&param_hashes, &result_hashes)
}

/// Compute the interface hash for an Arena containing functions.
///
/// The Arena is treated as an interface — its name, in-scope type definitions,
/// and function signatures are hashed to produce a content-addressed
/// interface hash. Named refs in function signatures resolve structurally
/// against `interface_arena.types`.
pub fn compute_interface_hash(interface_arena: &Arena) -> TypeHash {
    // Resolve refs against this interface's own typedefs.
    let types: &[TypeDef] = &interface_arena.types;

    // Create bindings for each function (sorted by name for determinism)
    let mut bindings: Vec<_> = interface_arena
        .functions
        .iter()
        .map(|f| Binding {
            name: &f.name,
            hash: hash_function_from_sig_in(f, types),
        })
        .collect();
    bindings.sort_by(|a, b| a.name.cmp(b.name));

    hash_interface(
        &interface_arena.name,
        &[], // No type bindings for now
        &bindings,
    )
}

/// Compute interface hashes for all interfaces in an Arena's imports or exports section.
///
/// Returns a list of (interface_name, interface_hash) pairs.
pub fn compute_interface_hashes(arena: &Arena, section: &str) -> Vec<InterfaceHash> {
    let mut result = Vec::new();

    for child in &arena.children {
        if child.name == section {
            // Each child of "imports" or "exports" is an interface
            for interface_arena in &child.children {
                result.push(InterfaceHash {
                    name: interface_arena.name.clone(),
                    hash: compute_interface_hash(interface_arena),
                });
            }
        }
    }

    result
}

/// Metadata with interface hashes for compatibility checking.
#[derive(Debug, Clone)]
pub struct MetadataWithHashes {
    /// The decoded arena (types, functions).
    pub arena: Arena,
    /// Hashes of imported interfaces.
    pub import_hashes: Vec<InterfaceHash>,
    /// Hashes of exported interfaces.
    pub export_hashes: Vec<InterfaceHash>,
}

/// Errors that can occur when reading metadata.
#[derive(Debug, thiserror::Error)]
pub enum MetadataError {
    #[error("package does not export __pack_types")]
    NotFound,

    #[error("metadata call failed: {0}")]
    CallFailed(String),

    #[error("failed to decode metadata: {0}")]
    DecodeFailed(String),

    #[error("invalid metadata structure: {0}")]
    InvalidStructure(String),

    #[error("failed to encode metadata: {0}")]
    EncodeFailed(String),
}

// ============================================================================
// CGRF Type Tags - Wire Format Compatibility
// ============================================================================

// These tag numbers MUST be preserved for backwards compatibility with existing
// WASM packages. The CGRF wire format uses these variant tags to encode types.
const TAG_BOOL: u32 = 0;
const TAG_U8: u32 = 1;
const TAG_U16: u32 = 2;
const TAG_U32: u32 = 3;
const TAG_U64: u32 = 4;
const TAG_S8: u32 = 5;
const TAG_S16: u32 = 6;
const TAG_S32: u32 = 7;
const TAG_S64: u32 = 8;
const TAG_F32: u32 = 9;
const TAG_F64: u32 = 10;
const TAG_CHAR: u32 = 11;
const TAG_STRING: u32 = 12;
const TAG_FLAGS: u32 = 13;
const TAG_LIST: u32 = 14;
const TAG_OPTION: u32 = 15;
const TAG_RESULT: u32 = 16;
const TAG_RECORD: u32 = 17;
const TAG_VARIANT: u32 = 18;
const TAG_TUPLE: u32 = 19;
const TAG_VALUE: u32 = 20;
const TAG_UNIT: u32 = 21;

// ============================================================================
// Metadata Decoding
// ============================================================================

/// Decode CGRF bytes into an Arena.
///
/// The metadata format is a record with "imports" and "exports" lists,
/// each containing function signatures. This is converted to an Arena
/// with two child arenas: one for imports, one for exports.
pub fn decode_metadata(bytes: &[u8]) -> Result<Arena, MetadataError> {
    let value = decode(bytes).map_err(|e| MetadataError::DecodeFailed(format!("{:?}", e)))?;

    match value {
        Value::Record { fields, .. } => {
            let mut imports = Vec::new();
            let mut exports = Vec::new();
            let mut type_defs = Vec::new();

            for (name, val) in fields {
                match name.as_str() {
                    "imports" => imports = decode_func_sig_list(val, &mut type_defs)?,
                    "exports" => exports = decode_func_sig_list(val, &mut type_defs)?,
                    _ => {}
                }
            }

            // Build an Arena with imports and exports as child arenas
            let mut arena = Arena::new("package");

            if !imports.is_empty() {
                let mut import_arena = Arena::new("imports");
                let mut by_interface: std::collections::HashMap<String, Vec<Function>> =
                    std::collections::HashMap::new();
                for (interface, func) in imports {
                    by_interface.entry(interface).or_default().push(func);
                }
                for (interface_name, funcs) in by_interface {
                    let mut interface_arena = Arena::new(interface_name);
                    for func in funcs {
                        interface_arena.add_function(func);
                    }
                    import_arena.add_child(interface_arena);
                }
                arena.add_child(import_arena);
            }

            if !exports.is_empty() {
                let mut export_arena = Arena::new("exports");
                let mut by_interface: std::collections::HashMap<String, Vec<Function>> =
                    std::collections::HashMap::new();
                for (interface, func) in exports {
                    by_interface.entry(interface).or_default().push(func);
                }
                for (interface_name, funcs) in by_interface {
                    let mut interface_arena = Arena::new(interface_name);
                    for func in funcs {
                        interface_arena.add_function(func);
                    }
                    export_arena.add_child(interface_arena);
                }
                arena.add_child(export_arena);
            }

            Ok(arena)
        }
        _ => Err(MetadataError::InvalidStructure(
            "expected record at top level".into(),
        )),
    }
}

/// Decode CGRF bytes into metadata with interface hashes.
///
/// This is the preferred decoding function as it includes Merkle-tree hashes
/// for O(1) interface compatibility checking.
pub fn decode_metadata_with_hashes(bytes: &[u8]) -> Result<MetadataWithHashes, MetadataError> {
    let value = decode(bytes).map_err(|e| MetadataError::DecodeFailed(format!("{:?}", e)))?;

    match value {
        Value::Record { fields, .. } => {
            let mut imports = Vec::new();
            let mut exports = Vec::new();
            let mut import_hashes = Vec::new();
            let mut export_hashes = Vec::new();
            let mut type_defs = Vec::new();

            for (name, val) in fields {
                match name.as_str() {
                    "imports" => imports = decode_func_sig_list(val, &mut type_defs)?,
                    "exports" => exports = decode_func_sig_list(val, &mut type_defs)?,
                    "import-hashes" => import_hashes = decode_interface_hash_list(val)?,
                    "export-hashes" => export_hashes = decode_interface_hash_list(val)?,
                    _ => {}
                }
            }

            // Build the arena (same as decode_metadata)
            let mut arena = Arena::new("package");

            if !imports.is_empty() {
                let mut import_arena = Arena::new("imports");
                let mut by_interface: std::collections::HashMap<String, Vec<Function>> =
                    std::collections::HashMap::new();
                for (interface, func) in imports {
                    by_interface.entry(interface).or_default().push(func);
                }
                for (interface_name, funcs) in by_interface {
                    let mut interface_arena = Arena::new(interface_name);
                    for func in funcs {
                        interface_arena.add_function(func);
                    }
                    import_arena.add_child(interface_arena);
                }
                arena.add_child(import_arena);
            }

            if !exports.is_empty() {
                let mut export_arena = Arena::new("exports");
                let mut by_interface: std::collections::HashMap<String, Vec<Function>> =
                    std::collections::HashMap::new();
                for (interface, func) in exports {
                    by_interface.entry(interface).or_default().push(func);
                }
                for (interface_name, funcs) in by_interface {
                    let mut interface_arena = Arena::new(interface_name);
                    for func in funcs {
                        interface_arena.add_function(func);
                    }
                    export_arena.add_child(interface_arena);
                }
                arena.add_child(export_arena);
            }

            Ok(MetadataWithHashes {
                arena,
                import_hashes,
                export_hashes,
            })
        }
        _ => Err(MetadataError::InvalidStructure(
            "expected record at top level".into(),
        )),
    }
}

/// Decode a list of interface hashes.
fn decode_interface_hash_list(value: Value) -> Result<Vec<InterfaceHash>, MetadataError> {
    match value {
        Value::List { items, .. } => items.into_iter().map(decode_interface_hash).collect(),
        _ => Err(MetadataError::InvalidStructure(
            "expected list of interface hashes".into(),
        )),
    }
}

/// Decode a single interface hash.
fn decode_interface_hash(value: Value) -> Result<InterfaceHash, MetadataError> {
    match value {
        Value::Record { fields, .. } => {
            let mut name = String::new();
            let mut hash = TypeHash::from_bytes([0u8; 32]);

            for (field_name, val) in fields {
                match field_name.as_str() {
                    "name" => {
                        if let Value::String(s) = val {
                            name = s;
                        }
                    }
                    "hash" => {
                        // Hash can be stored as list<u8> or tuple<u64, u64, u64, u64>
                        match val {
                            Value::List { items, .. } => {
                                // List of u8 bytes
                                let bytes: Vec<u8> = items
                                    .into_iter()
                                    .filter_map(|v| match v {
                                        Value::U8(b) => Some(b),
                                        _ => None,
                                    })
                                    .collect();
                                if bytes.len() == 32 {
                                    let mut arr = [0u8; 32];
                                    arr.copy_from_slice(&bytes);
                                    hash = TypeHash::from_bytes(arr);
                                }
                            }
                            Value::Tuple(parts) => {
                                // Legacy: tuple of 4 u64s
                                if parts.len() == 4 {
                                    let a = match &parts[0] {
                                        Value::U64(v) => *v,
                                        _ => 0,
                                    };
                                    let b = match &parts[1] {
                                        Value::U64(v) => *v,
                                        _ => 0,
                                    };
                                    let c = match &parts[2] {
                                        Value::U64(v) => *v,
                                        _ => 0,
                                    };
                                    let d = match &parts[3] {
                                        Value::U64(v) => *v,
                                        _ => 0,
                                    };
                                    hash = TypeHash::from_u64s(a, b, c, d);
                                }
                            }
                            _ => {}
                        }
                    }
                    _ => {}
                }
            }

            Ok(InterfaceHash { name, hash })
        }
        _ => Err(MetadataError::InvalidStructure(
            "expected record for interface hash".into(),
        )),
    }
}

/// Decode a list of function signatures.
/// Returns (interface_name, Function) pairs.
fn decode_func_sig_list(
    value: Value,
    type_defs: &mut Vec<TypeDef>,
) -> Result<Vec<(String, Function)>, MetadataError> {
    match value {
        Value::List { items, .. } => items
            .into_iter()
            .map(|v| decode_func_sig(v, type_defs))
            .collect(),
        _ => Err(MetadataError::InvalidStructure(
            "expected list of function signatures".into(),
        )),
    }
}

/// Decode a function signature, collecting discovered TypeDefs.
/// Returns (interface_name, Function).
fn decode_func_sig(
    value: Value,
    type_defs: &mut Vec<TypeDef>,
) -> Result<(String, Function), MetadataError> {
    match value {
        Value::Record { fields, .. } => {
            let mut interface = String::new();
            let mut name = String::new();
            let mut params = Vec::new();
            let mut results = Vec::new();

            for (field_name, val) in fields {
                match field_name.as_str() {
                    "interface" => {
                        if let Value::String(s) = val {
                            interface = s;
                        }
                    }
                    "name" => {
                        if let Value::String(s) = val {
                            name = s;
                        }
                    }
                    "params" => {
                        params = decode_param_list(val, type_defs)?;
                    }
                    "results" => {
                        results = decode_type_list(val, type_defs)?;
                    }
                    _ => {}
                }
            }

            let mut func = Function::with_signature(name, params, results);
            // Attach discovered type definitions to the function
            func.types = type_defs.clone();

            Ok((interface, func))
        }
        _ => Err(MetadataError::InvalidStructure(
            "expected record for function signature".into(),
        )),
    }
}

fn decode_param_list(
    value: Value,
    type_defs: &mut Vec<TypeDef>,
) -> Result<Vec<Param>, MetadataError> {
    match value {
        Value::List { items, .. } => items
            .into_iter()
            .map(|v| decode_param(v, type_defs))
            .collect(),
        _ => Err(MetadataError::InvalidStructure(
            "expected list of parameters".into(),
        )),
    }
}

fn decode_param(value: Value, type_defs: &mut Vec<TypeDef>) -> Result<Param, MetadataError> {
    match value {
        Value::Record { fields, .. } => {
            let mut name = String::new();
            let mut ty = Type::Value;

            for (field_name, val) in fields {
                match field_name.as_str() {
                    "name" => {
                        if let Value::String(s) = val {
                            name = s;
                        }
                    }
                    "type" => {
                        ty = decode_type_collecting(val, type_defs)?;
                    }
                    _ => {}
                }
            }

            Ok(Param::new(name, ty))
        }
        _ => Err(MetadataError::InvalidStructure(
            "expected record for parameter".into(),
        )),
    }
}

fn decode_type_list(
    value: Value,
    type_defs: &mut Vec<TypeDef>,
) -> Result<Vec<Type>, MetadataError> {
    match value {
        Value::List { items, .. } => items
            .into_iter()
            .map(|v| decode_type_collecting(v, type_defs))
            .collect(),
        _ => Err(MetadataError::InvalidStructure(
            "expected list of types".into(),
        )),
    }
}

fn decode_type_collecting(
    value: Value,
    type_defs: &mut Vec<TypeDef>,
) -> Result<Type, MetadataError> {
    match value {
        Value::Variant { tag, payload, .. } => {
            let tag = tag as u32;
            match tag {
                TAG_BOOL => Ok(Type::Bool),
                TAG_U8 => Ok(Type::U8),
                TAG_U16 => Ok(Type::U16),
                TAG_U32 => Ok(Type::U32),
                TAG_U64 => Ok(Type::U64),
                TAG_S8 => Ok(Type::S8),
                TAG_S16 => Ok(Type::S16),
                TAG_S32 => Ok(Type::S32),
                TAG_S64 => Ok(Type::S64),
                TAG_F32 => Ok(Type::F32),
                TAG_F64 => Ok(Type::F64),
                TAG_CHAR => Ok(Type::Char),
                TAG_STRING => Ok(Type::String),
                TAG_FLAGS => Ok(Type::Ref(TypePath::simple("flags"))),
                TAG_LIST => {
                    let inner = payload.into_iter().next().ok_or_else(|| {
                        MetadataError::InvalidStructure("list missing element type".into())
                    })?;
                    Ok(Type::list(decode_type_collecting(inner, type_defs)?))
                }
                TAG_OPTION => {
                    let inner = payload.into_iter().next().ok_or_else(|| {
                        MetadataError::InvalidStructure("option missing inner type".into())
                    })?;
                    Ok(Type::option(decode_type_collecting(inner, type_defs)?))
                }
                TAG_RESULT => {
                    let record = payload.into_iter().next().ok_or_else(|| {
                        MetadataError::InvalidStructure("result missing payload".into())
                    })?;
                    match record {
                        Value::Record { fields, .. } => {
                            let mut ok = Type::Unit;
                            let mut err = Type::Unit;
                            for (name, val) in fields {
                                match name.as_str() {
                                    "ok" => ok = decode_type_collecting(val, type_defs)?,
                                    "err" => err = decode_type_collecting(val, type_defs)?,
                                    _ => {}
                                }
                            }
                            Ok(Type::result(ok, err))
                        }
                        _ => Err(MetadataError::InvalidStructure(
                            "result payload not a record".into(),
                        )),
                    }
                }
                TAG_RECORD => {
                    let record = payload.into_iter().next().ok_or_else(|| {
                        MetadataError::InvalidStructure("record missing payload".into())
                    })?;
                    decode_record_type(record, type_defs)
                }
                TAG_VARIANT => {
                    let record = payload.into_iter().next().ok_or_else(|| {
                        MetadataError::InvalidStructure("variant missing payload".into())
                    })?;
                    decode_variant_type(record, type_defs)
                }
                TAG_TUPLE => {
                    let list = payload.into_iter().next().ok_or_else(|| {
                        MetadataError::InvalidStructure("tuple missing payload".into())
                    })?;
                    match list {
                        Value::List { items, .. } => {
                            let types: Result<Vec<_>, _> = items
                                .into_iter()
                                .map(|v| decode_type_collecting(v, type_defs))
                                .collect();
                            Ok(Type::tuple(types?))
                        }
                        _ => Err(MetadataError::InvalidStructure(
                            "tuple payload not a list".into(),
                        )),
                    }
                }
                TAG_VALUE => Ok(Type::Value),
                TAG_UNIT => Ok(Type::Unit),
                _ => Err(MetadataError::InvalidStructure(format!(
                    "unknown type tag: {}",
                    tag
                ))),
            }
        }
        _ => Err(MetadataError::InvalidStructure(
            "expected variant for type".into(),
        )),
    }
}

fn decode_record_type(value: Value, type_defs: &mut Vec<TypeDef>) -> Result<Type, MetadataError> {
    match value {
        Value::Record {
            fields: rec_fields, ..
        } => {
            let mut name = String::new();
            let mut decoded_fields = Vec::new();

            for (fname, val) in rec_fields {
                match fname.as_str() {
                    "name" => {
                        if let Value::String(s) = val {
                            name = s;
                        }
                    }
                    "fields" => {
                        if let Value::List { items, .. } = val {
                            for item in items {
                                if let Value::Record {
                                    fields: field_rec, ..
                                } = item
                                {
                                    let mut field_name = String::new();
                                    let mut field_type = Type::Value;
                                    for (fn_name, fn_val) in field_rec {
                                        match fn_name.as_str() {
                                            "name" => {
                                                if let Value::String(s) = fn_val {
                                                    field_name = s;
                                                }
                                            }
                                            "type" => {
                                                field_type =
                                                    decode_type_collecting(fn_val, type_defs)?;
                                            }
                                            _ => {}
                                        }
                                    }
                                    decoded_fields.push(Field::new(field_name, field_type));
                                }
                            }
                        }
                    }
                    _ => {}
                }
            }

            // Store the TypeDef for later resolution
            if !name.is_empty() && !decoded_fields.is_empty() {
                // Only add if we don't already have this type
                if !type_defs.iter().any(|td| td.name() == name) {
                    type_defs.push(TypeDef::Record {
                        name: name.clone(),
                        fields: decoded_fields,
                    });
                }
            }

            Ok(Type::Ref(TypePath::simple(name)))
        }
        _ => Err(MetadataError::InvalidStructure(
            "record payload not a record".into(),
        )),
    }
}

fn decode_variant_type(value: Value, type_defs: &mut Vec<TypeDef>) -> Result<Type, MetadataError> {
    match value {
        Value::Record {
            fields: rec_fields, ..
        } => {
            let mut name = String::new();
            let mut decoded_cases = Vec::new();

            for (fname, val) in rec_fields {
                match fname.as_str() {
                    "name" => {
                        if let Value::String(s) = val {
                            name = s;
                        }
                    }
                    "cases" => {
                        if let Value::List { items, .. } = val {
                            for item in items {
                                if let Value::Record {
                                    fields: case_rec, ..
                                } = item
                                {
                                    let mut case_name = String::new();
                                    let mut case_payload = Type::Unit;
                                    for (cn, cv) in case_rec {
                                        match cn.as_str() {
                                            "name" => {
                                                if let Value::String(s) = cv {
                                                    case_name = s;
                                                }
                                            }
                                            "payload" => {
                                                if let Value::Option {
                                                    value: Some(payload_val),
                                                    ..
                                                } = cv
                                                {
                                                    case_payload = decode_type_collecting(
                                                        *payload_val,
                                                        type_defs,
                                                    )?;
                                                }
                                            }
                                            _ => {}
                                        }
                                    }
                                    decoded_cases.push(Case::new(case_name, case_payload));
                                }
                            }
                        }
                    }
                    _ => {}
                }
            }

            // Store the TypeDef for later resolution
            if !name.is_empty()
                && !decoded_cases.is_empty()
                && !type_defs.iter().any(|td| td.name() == name)
            {
                type_defs.push(TypeDef::Variant {
                    name: name.clone(),
                    cases: decoded_cases,
                });
            }

            Ok(Type::Ref(TypePath::simple(name)))
        }
        _ => Err(MetadataError::InvalidStructure(
            "variant payload not a record".into(),
        )),
    }
}

// ============================================================================
// Metadata Encoding
// ============================================================================

/// Encode an Arena to CGRF bytes (metadata format).
///
/// The Arena is encoded as a record with "imports" and "exports" lists.
/// Child arenas named "imports" and "exports" are used, with their children
/// as interfaces containing functions.
pub fn encode_metadata(arena: &Arena) -> Result<Vec<u8>, MetadataError> {
    let mut imports = Vec::new();
    let mut exports = Vec::new();

    for child in &arena.children {
        match child.name.as_str() {
            "imports" => {
                for interface in &child.children {
                    for func in &interface.functions {
                        imports.push(encode_func_sig_value(&interface.name, func));
                    }
                }
            }
            "exports" => {
                for interface in &child.children {
                    for func in &interface.functions {
                        exports.push(encode_func_sig_value(&interface.name, func));
                    }
                }
            }
            _ => {}
        }
    }

    let record = Value::Record {
        type_name: "PackageMetadata".to_string(),
        fields: vec![
            (
                "imports".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Record("FunctionSignature".to_string()),
                    items: imports,
                },
            ),
            (
                "exports".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Record("FunctionSignature".to_string()),
                    items: exports,
                },
            ),
        ],
    };

    encode(&record).map_err(|e| MetadataError::EncodeFailed(format!("{:?}", e)))
}

fn encode_func_sig_value(interface: &str, func: &Function) -> Value {
    Value::Record {
        type_name: "FunctionSignature".to_string(),
        fields: vec![
            (
                "interface".to_string(),
                Value::String(interface.to_string()),
            ),
            ("name".to_string(), Value::String(func.name.clone())),
            (
                "params".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Record("ParamSignature".to_string()),
                    items: func.params.iter().map(encode_param_value).collect(),
                },
            ),
            (
                "results".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Variant("Type".to_string()),
                    items: func.results.iter().map(encode_type_value).collect(),
                },
            ),
        ],
    }
}

fn encode_param_value(param: &Param) -> Value {
    Value::Record {
        type_name: "ParamSignature".to_string(),
        fields: vec![
            ("name".to_string(), Value::String(param.name.clone())),
            ("type".to_string(), encode_type_value(&param.ty)),
        ],
    }
}

fn encode_type_value(ty: &Type) -> Value {
    let (tag, payload) = match ty {
        Type::Unit => (TAG_UNIT as usize, vec![]),
        Type::Bool => (TAG_BOOL as usize, vec![]),
        Type::U8 => (TAG_U8 as usize, vec![]),
        Type::U16 => (TAG_U16 as usize, vec![]),
        Type::U32 => (TAG_U32 as usize, vec![]),
        Type::U64 => (TAG_U64 as usize, vec![]),
        Type::S8 => (TAG_S8 as usize, vec![]),
        Type::S16 => (TAG_S16 as usize, vec![]),
        Type::S32 => (TAG_S32 as usize, vec![]),
        Type::S64 => (TAG_S64 as usize, vec![]),
        Type::F32 => (TAG_F32 as usize, vec![]),
        Type::F64 => (TAG_F64 as usize, vec![]),
        Type::Char => (TAG_CHAR as usize, vec![]),
        Type::String => (TAG_STRING as usize, vec![]),
        Type::List(inner) => (TAG_LIST as usize, vec![encode_type_value(inner)]),
        Type::Option(inner) => (TAG_OPTION as usize, vec![encode_type_value(inner)]),
        Type::Result { ok, err } => (
            TAG_RESULT as usize,
            vec![Value::Record {
                type_name: "ResultPayload".to_string(),
                fields: vec![
                    ("ok".to_string(), encode_type_value(ok)),
                    ("err".to_string(), encode_type_value(err)),
                ],
            }],
        ),
        Type::Tuple(types) => (
            TAG_TUPLE as usize,
            vec![Value::List {
                elem_type: crate::abi::ValueType::Variant("Type".to_string()),
                items: types.iter().map(encode_type_value).collect(),
            }],
        ),
        Type::Ref(path) => {
            let name = path.segments.join("::");
            (
                TAG_VARIANT as usize,
                vec![Value::Record {
                    type_name: "TypeRef".to_string(),
                    fields: vec![("name".to_string(), Value::String(name))],
                }],
            )
        }
        Type::Value => (TAG_VALUE as usize, vec![]),
    };

    Value::Variant {
        type_name: "Type".to_string(),
        case_name: format!("tag{}", tag),
        tag,
        payload,
    }
}

// ============================================================================
// Legacy Type Aliases (for backward compatibility during migration)
// ============================================================================

/// Legacy type alias for backward compatibility.
/// Use `crate::types::Type` instead.
pub type TypeDesc = Type;

/// Legacy type alias for backward compatibility.
/// Use `crate::types::Field` instead.
pub type FieldDesc = Field;

/// Legacy type alias for backward compatibility.
/// Use `crate::types::Case` instead.
pub type CaseDesc = Case;

/// Legacy type alias for backward compatibility.
/// Use `crate::types::Param` instead.
pub type ParamSignature = Param;

/// Legacy type alias for backward compatibility.
/// Use `crate::types::Function` instead.
pub type FunctionSignature = Function;

/// Legacy type alias for backward compatibility.
/// Use `crate::types::Arena` instead.
pub type PackageMetadata = Arena;

// ============================================================================
// Metadata Encoding with Hashes
// ============================================================================

/// Encode an Arena to CGRF bytes with interface hashes included.
///
/// This is the preferred encoding function as it includes Merkle-tree hashes
/// for O(1) interface compatibility checking at runtime.
///
/// The output format includes:
/// - `imports`: List of function signatures
/// - `exports`: List of function signatures
/// - `import-hashes`: List of (interface_name, hash) pairs
/// - `export-hashes`: List of (interface_name, hash) pairs
pub fn encode_metadata_with_hashes(arena: &Arena) -> Result<Vec<u8>, MetadataError> {
    let mut imports = Vec::new();
    let mut exports = Vec::new();

    // Collect function signatures (same as encode_metadata)
    for child in &arena.children {
        match child.name.as_str() {
            "imports" => {
                for interface in &child.children {
                    for func in &interface.functions {
                        imports.push(encode_func_sig_value(&interface.name, func));
                    }
                }
            }
            "exports" => {
                for interface in &child.children {
                    for func in &interface.functions {
                        exports.push(encode_func_sig_value(&interface.name, func));
                    }
                }
            }
            _ => {}
        }
    }

    // Compute interface hashes
    let import_hashes = compute_interface_hashes(arena, "imports");
    let export_hashes = compute_interface_hashes(arena, "exports");

    let record = Value::Record {
        type_name: "PackageMetadata".to_string(),
        fields: vec![
            (
                "imports".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Record("FunctionSignature".to_string()),
                    items: imports,
                },
            ),
            (
                "exports".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Record("FunctionSignature".to_string()),
                    items: exports,
                },
            ),
            (
                "import-hashes".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Record("InterfaceHash".to_string()),
                    items: import_hashes
                        .iter()
                        .map(encode_interface_hash_value)
                        .collect(),
                },
            ),
            (
                "export-hashes".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::Record("InterfaceHash".to_string()),
                    items: export_hashes
                        .iter()
                        .map(encode_interface_hash_value)
                        .collect(),
                },
            ),
        ],
    };

    encode(&record).map_err(|e| MetadataError::EncodeFailed(format!("{:?}", e)))
}

/// Encode an InterfaceHash as a CGRF Value.
fn encode_interface_hash_value(ih: &InterfaceHash) -> Value {
    Value::Record {
        type_name: "InterfaceHash".to_string(),
        fields: vec![
            ("name".to_string(), Value::String(ih.name.clone())),
            (
                "hash".to_string(),
                Value::List {
                    elem_type: crate::abi::ValueType::U8,
                    items: ih.hash.as_bytes().iter().map(|&b| Value::U8(b)).collect(),
                },
            ),
        ],
    }
}

// ============================================================================
// Type Space Validation
// ============================================================================

/// Errors from validating a Value against a type space.
#[derive(Debug, Clone)]
pub enum TypeValidationError {
    /// Value's runtime type doesn't match the expected type.
    TypeMismatch { expected: String, got: String },
    /// Record is missing a required field.
    MissingField { record: String, field: String },
    /// Record has a field not declared in the type.
    ExtraField { record: String, field: String },
    /// Variant case name not found in the type definition.
    UnknownCase { variant: String, case: String },
    /// Type::Ref could not be resolved to a TypeDef.
    UnresolvedRef { path: String },
    /// Tuple or payload length mismatch.
    WrongArity { expected: usize, got: usize },
    /// Error in a nested position, with context path.
    Nested {
        context: String,
        inner: Box<TypeValidationError>,
    },
}

impl std::fmt::Display for TypeValidationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TypeValidationError::TypeMismatch { expected, got } => {
                write!(f, "expected {}, got {}", expected, got)
            }
            TypeValidationError::MissingField { record, field } => {
                write!(f, "record '{}' missing field '{}'", record, field)
            }
            TypeValidationError::ExtraField { record, field } => {
                write!(f, "record '{}' has unexpected field '{}'", record, field)
            }
            TypeValidationError::UnknownCase { variant, case } => {
                write!(f, "variant '{}' has no case '{}'", variant, case)
            }
            TypeValidationError::UnresolvedRef { path } => {
                write!(f, "unresolved type reference '{}'", path)
            }
            TypeValidationError::WrongArity { expected, got } => {
                write!(f, "expected {} elements, got {}", expected, got)
            }
            TypeValidationError::Nested { context, inner } => {
                write!(f, "in {}: {}", context, inner)
            }
        }
    }
}

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

/// Validate that a runtime Value is a valid inhabitant of the given type space.
///
/// The type space is defined by an expected `Type` and a set of `TypeDef`s
/// that resolve named references (`Type::Ref`). The Value represents a concrete
/// instance — this function checks membership.
///
/// `Type::Value` is the escape hatch: any Value is valid in that position.
pub fn validate_value_in_type_space(
    value: &Value,
    expected: &Type,
    type_defs: &[TypeDef],
) -> Result<(), TypeValidationError> {
    match expected {
        // Escape hatch — anything goes
        Type::Value => Ok(()),

        // Primitives — direct type match
        Type::Unit => match value {
            Value::Tuple(items) if items.is_empty() => Ok(()),
            _ => Err(TypeValidationError::TypeMismatch {
                expected: "unit".into(),
                got: value_type_name(value),
            }),
        },
        Type::Bool => match value {
            Value::Bool(_) => Ok(()),
            _ => Err(mismatch("bool", value)),
        },
        Type::U8 => match value {
            Value::U8(_) => Ok(()),
            _ => Err(mismatch("u8", value)),
        },
        Type::U16 => match value {
            Value::U16(_) => Ok(()),
            _ => Err(mismatch("u16", value)),
        },
        Type::U32 => match value {
            Value::U32(_) => Ok(()),
            _ => Err(mismatch("u32", value)),
        },
        Type::U64 => match value {
            Value::U64(_) => Ok(()),
            _ => Err(mismatch("u64", value)),
        },
        Type::S8 => match value {
            Value::S8(_) => Ok(()),
            _ => Err(mismatch("s8", value)),
        },
        Type::S16 => match value {
            Value::S16(_) => Ok(()),
            _ => Err(mismatch("s16", value)),
        },
        Type::S32 => match value {
            Value::S32(_) => Ok(()),
            _ => Err(mismatch("s32", value)),
        },
        Type::S64 => match value {
            Value::S64(_) => Ok(()),
            _ => Err(mismatch("s64", value)),
        },
        Type::F32 => match value {
            Value::F32(_) => Ok(()),
            _ => Err(mismatch("f32", value)),
        },
        Type::F64 => match value {
            Value::F64(_) => Ok(()),
            _ => Err(mismatch("f64", value)),
        },
        Type::Char => match value {
            Value::Char(_) => Ok(()),
            _ => Err(mismatch("char", value)),
        },
        Type::String => match value {
            Value::String(_) => Ok(()),
            _ => Err(mismatch("string", value)),
        },

        // Compound types — recurse
        Type::List(elem_type) => match value {
            Value::List { items, .. } => {
                for (i, item) in items.iter().enumerate() {
                    validate_value_in_type_space(item, elem_type, type_defs).map_err(|e| {
                        TypeValidationError::Nested {
                            context: format!("list[{}]", i),
                            inner: Box::new(e),
                        }
                    })?;
                }
                Ok(())
            }
            _ => Err(mismatch("list", value)),
        },

        Type::Option(inner_type) => match value {
            Value::Option { value: inner, .. } => {
                if let Some(v) = inner {
                    validate_value_in_type_space(v, inner_type, type_defs).map_err(|e| {
                        TypeValidationError::Nested {
                            context: "option::some".into(),
                            inner: Box::new(e),
                        }
                    })?;
                }
                Ok(())
            }
            _ => Err(mismatch("option", value)),
        },

        Type::Result { ok, err } => match value {
            Value::Result { value: result, .. } => {
                match result {
                    Ok(v) => validate_value_in_type_space(v, ok, type_defs).map_err(|e| {
                        TypeValidationError::Nested {
                            context: "result::ok".into(),
                            inner: Box::new(e),
                        }
                    })?,
                    Err(v) => validate_value_in_type_space(v, err, type_defs).map_err(|e| {
                        TypeValidationError::Nested {
                            context: "result::err".into(),
                            inner: Box::new(e),
                        }
                    })?,
                }
                Ok(())
            }
            _ => Err(mismatch("result", value)),
        },

        Type::Tuple(types) => match value {
            Value::Tuple(items) => {
                if types.len() != items.len() {
                    return Err(TypeValidationError::WrongArity {
                        expected: types.len(),
                        got: items.len(),
                    });
                }
                for (i, (ty, val)) in types.iter().zip(items.iter()).enumerate() {
                    validate_value_in_type_space(val, ty, type_defs).map_err(|e| {
                        TypeValidationError::Nested {
                            context: format!("tuple.{}", i),
                            inner: Box::new(e),
                        }
                    })?;
                }
                Ok(())
            }
            _ => Err(mismatch("tuple", value)),
        },

        // Named reference — resolve and validate against the definition
        Type::Ref(path) => {
            let name = path.segments.last().map(|s| s.as_str()).unwrap_or("");
            let type_def = type_defs.iter().find(|td| td.name() == name);

            match type_def {
                Some(TypeDef::Record { name, fields }) => {
                    validate_record(value, name, fields, type_defs)
                }
                Some(TypeDef::Variant { name, cases }) => {
                    validate_variant(value, name, cases, type_defs)
                }
                Some(TypeDef::Alias { ty, .. }) => {
                    validate_value_in_type_space(value, ty, type_defs)
                }
                Some(TypeDef::Enum { name, cases }) => validate_enum(value, name, cases),
                Some(TypeDef::Flags { .. }) => match value {
                    Value::Flags(_) => Ok(()),
                    _ => Err(mismatch("flags", value)),
                },
                None => Err(TypeValidationError::UnresolvedRef {
                    path: path.to_string(),
                }),
            }
        }
    }
}

fn validate_record(
    value: &Value,
    record_name: &str,
    expected_fields: &[Field],
    type_defs: &[TypeDef],
) -> Result<(), TypeValidationError> {
    match value {
        Value::Record {
            fields: val_fields, ..
        } => {
            // Check for missing fields
            for expected in expected_fields {
                if !val_fields.iter().any(|(name, _)| name == &expected.name) {
                    return Err(TypeValidationError::MissingField {
                        record: record_name.into(),
                        field: expected.name.clone(),
                    });
                }
            }
            // Check for extra fields
            for (name, _) in val_fields {
                if !expected_fields.iter().any(|f| &f.name == name) {
                    return Err(TypeValidationError::ExtraField {
                        record: record_name.into(),
                        field: name.clone(),
                    });
                }
            }
            // Validate each field's value
            for (name, val) in val_fields {
                if let Some(field_def) = expected_fields.iter().find(|f| &f.name == name) {
                    validate_value_in_type_space(val, &field_def.ty, type_defs).map_err(|e| {
                        TypeValidationError::Nested {
                            context: format!("field '{}'", name),
                            inner: Box::new(e),
                        }
                    })?;
                }
            }
            Ok(())
        }
        _ => Err(TypeValidationError::TypeMismatch {
            expected: format!("record '{}'", record_name),
            got: value_type_name(value),
        }),
    }
}

fn validate_variant(
    value: &Value,
    variant_name: &str,
    expected_cases: &[Case],
    type_defs: &[TypeDef],
) -> Result<(), TypeValidationError> {
    match value {
        Value::Variant {
            case_name, payload, ..
        } => {
            let case_def = expected_cases.iter().find(|c| &c.name == case_name);
            match case_def {
                Some(case) => {
                    // Validate payload against the case's declared type
                    match (&case.payload, payload.len()) {
                        (Type::Unit, 0) => Ok(()),
                        (Type::Unit, n) => Err(TypeValidationError::Nested {
                            context: format!("case '{}'", case_name),
                            inner: Box::new(TypeValidationError::WrongArity {
                                expected: 0,
                                got: n,
                            }),
                        }),
                        (ty, 1) => validate_value_in_type_space(&payload[0], ty, type_defs)
                            .map_err(|e| TypeValidationError::Nested {
                                context: format!("case '{}'", case_name),
                                inner: Box::new(e),
                            }),
                        (_, n) => Err(TypeValidationError::Nested {
                            context: format!("case '{}'", case_name),
                            inner: Box::new(TypeValidationError::WrongArity {
                                expected: 1,
                                got: n,
                            }),
                        }),
                    }
                }
                None => Err(TypeValidationError::UnknownCase {
                    variant: variant_name.into(),
                    case: case_name.clone(),
                }),
            }
        }
        _ => Err(TypeValidationError::TypeMismatch {
            expected: format!("variant '{}'", variant_name),
            got: value_type_name(value),
        }),
    }
}

fn validate_enum(
    value: &Value,
    enum_name: &str,
    expected_cases: &[String],
) -> Result<(), TypeValidationError> {
    match value {
        Value::Variant {
            case_name, payload, ..
        } => {
            if !expected_cases.iter().any(|c| c == case_name) {
                return Err(TypeValidationError::UnknownCase {
                    variant: enum_name.into(),
                    case: case_name.clone(),
                });
            }
            if !payload.is_empty() {
                return Err(TypeValidationError::Nested {
                    context: format!("enum case '{}'", case_name),
                    inner: Box::new(TypeValidationError::WrongArity {
                        expected: 0,
                        got: payload.len(),
                    }),
                });
            }
            Ok(())
        }
        _ => Err(TypeValidationError::TypeMismatch {
            expected: format!("enum '{}'", enum_name),
            got: value_type_name(value),
        }),
    }
}

fn mismatch(expected: &str, value: &Value) -> TypeValidationError {
    TypeValidationError::TypeMismatch {
        expected: expected.into(),
        got: value_type_name(value),
    }
}

fn value_type_name(value: &Value) -> String {
    match value {
        Value::Bool(_) => "bool".into(),
        Value::U8(_) => "u8".into(),
        Value::U16(_) => "u16".into(),
        Value::U32(_) => "u32".into(),
        Value::U64(_) => "u64".into(),
        Value::S8(_) => "s8".into(),
        Value::S16(_) => "s16".into(),
        Value::S32(_) => "s32".into(),
        Value::S64(_) => "s64".into(),
        Value::F32(_) => "f32".into(),
        Value::F64(_) => "f64".into(),
        Value::Char(_) => "char".into(),
        Value::String(_) => "string".into(),
        Value::List { .. } => "list".into(),
        Value::Option { .. } => "option".into(),
        Value::Result { .. } => "result".into(),
        Value::Record { type_name, .. } => format!("record '{}'", type_name),
        Value::Variant {
            type_name,
            case_name,
            ..
        } => format!("variant '{}' (case '{}')", type_name, case_name),
        Value::Tuple(items) => format!("tuple<{}>", items.len()),
        Value::Flags(_) => "flags".into(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_type_tags_preserved() {
        // Verify tag constants match expected values for wire format compatibility
        assert_eq!(TAG_BOOL, 0);
        assert_eq!(TAG_U8, 1);
        assert_eq!(TAG_STRING, 12);
        assert_eq!(TAG_VALUE, 20);
        assert_eq!(TAG_UNIT, 21);
    }

    #[test]
    fn test_hash_type_primitives() {
        assert_eq!(hash_type(&Type::Bool), HASH_BOOL);
        assert_eq!(hash_type(&Type::String), HASH_STRING);
        assert_eq!(hash_type(&Type::S32), HASH_S32);
        assert_eq!(hash_type(&Type::U8), HASH_U8);
    }

    #[test]
    fn test_hash_type_compound() {
        let list_hash = hash_type(&Type::List(Box::new(Type::U8)));
        let expected = hash_list(&HASH_U8);
        assert_eq!(list_hash, expected);

        let option_hash = hash_type(&Type::Option(Box::new(Type::String)));
        let expected = hash_option(&HASH_STRING);
        assert_eq!(option_hash, expected);

        let result_hash = hash_type(&Type::Result {
            ok: Box::new(Type::String),
            err: Box::new(Type::String),
        });
        let expected = hash_result(&HASH_STRING, &HASH_STRING);
        assert_eq!(result_hash, expected);
    }

    #[test]
    fn test_compute_interface_hash() {
        // Create a simple interface arena with two functions
        let mut interface = Arena::new("test:example/api");
        interface.add_function(Function::with_signature(
            "greet",
            vec![Param::new("name", Type::String)],
            vec![Type::String],
        ));
        interface.add_function(Function::with_signature(
            "add",
            vec![Param::new("a", Type::S32), Param::new("b", Type::S32)],
            vec![Type::S32],
        ));

        let hash1 = compute_interface_hash(&interface);

        // Same interface should produce same hash
        let hash2 = compute_interface_hash(&interface);
        assert_eq!(hash1, hash2);

        // Different function order should produce same hash (sorted by name)
        let mut interface_reordered = Arena::new("test:example/api");
        interface_reordered.add_function(Function::with_signature(
            "add",
            vec![Param::new("a", Type::S32), Param::new("b", Type::S32)],
            vec![Type::S32],
        ));
        interface_reordered.add_function(Function::with_signature(
            "greet",
            vec![Param::new("name", Type::String)],
            vec![Type::String],
        ));
        let hash3 = compute_interface_hash(&interface_reordered);
        assert_eq!(hash1, hash3);
    }

    #[test]
    fn test_compute_interface_hash_differs_on_signature() {
        let mut interface1 = Arena::new("test:api");
        interface1.add_function(Function::with_signature(
            "foo",
            vec![Param::new("x", Type::S32)],
            vec![Type::S32],
        ));

        let mut interface2 = Arena::new("test:api");
        interface2.add_function(Function::with_signature(
            "foo",
            vec![Param::new("x", Type::S64)], // Different type!
            vec![Type::S64],
        ));

        assert_ne!(
            compute_interface_hash(&interface1),
            compute_interface_hash(&interface2)
        );
    }

    #[test]
    fn test_encode_metadata_with_hashes_roundtrip() {
        // Build an arena with imports and exports
        let mut arena = Arena::new("package");

        let mut imports = Arena::new("imports");
        let mut runtime_interface = Arena::new("theater:simple/runtime");
        runtime_interface.add_function(Function::with_signature(
            "log",
            vec![Param::new("msg", Type::String)],
            vec![],
        ));
        imports.add_child(runtime_interface);
        arena.add_child(imports);

        let mut exports = Arena::new("exports");
        let mut actor_interface = Arena::new("theater:simple/actor");
        actor_interface.add_function(Function::with_signature(
            "init",
            vec![Param::new("data", Type::List(Box::new(Type::U8)))],
            vec![Type::List(Box::new(Type::U8))],
        ));
        exports.add_child(actor_interface);
        arena.add_child(exports);

        // Encode with hashes
        let bytes = encode_metadata_with_hashes(&arena).expect("encoding failed");

        // Decode and verify hashes are present
        let decoded = decode_metadata_with_hashes(&bytes).expect("decoding failed");

        assert_eq!(decoded.import_hashes.len(), 1);
        assert_eq!(decoded.import_hashes[0].name, "theater:simple/runtime");

        assert_eq!(decoded.export_hashes.len(), 1);
        assert_eq!(decoded.export_hashes[0].name, "theater:simple/actor");

        // Verify hashes are non-zero
        assert!(!decoded.import_hashes[0]
            .hash
            .as_bytes()
            .iter()
            .all(|&b| b == 0));
        assert!(!decoded.export_hashes[0]
            .hash
            .as_bytes()
            .iter()
            .all(|&b| b == 0));
    }

    // ========================================================================
    // Type space validation tests
    // ========================================================================

    #[test]
    fn test_validate_primitives() {
        let defs = vec![];
        assert!(validate_value_in_type_space(&Value::Bool(true), &Type::Bool, &defs).is_ok());
        assert!(validate_value_in_type_space(&Value::U32(42), &Type::U32, &defs).is_ok());
        assert!(
            validate_value_in_type_space(&Value::String("hi".into()), &Type::String, &defs).is_ok()
        );

        // Mismatch
        assert!(validate_value_in_type_space(&Value::Bool(true), &Type::U32, &defs).is_err());
        assert!(validate_value_in_type_space(&Value::U32(42), &Type::String, &defs).is_err());
    }

    #[test]
    fn test_validate_unit() {
        let defs = vec![];
        assert!(validate_value_in_type_space(&Value::Tuple(vec![]), &Type::Unit, &defs).is_ok());
        assert!(validate_value_in_type_space(&Value::Bool(true), &Type::Unit, &defs).is_err());
    }

    #[test]
    fn test_validate_type_value_escape_hatch() {
        let defs = vec![];
        // Type::Value accepts anything
        assert!(validate_value_in_type_space(&Value::Bool(true), &Type::Value, &defs).is_ok());
        assert!(
            validate_value_in_type_space(&Value::String("x".into()), &Type::Value, &defs).is_ok()
        );
        assert!(validate_value_in_type_space(
            &Value::Tuple(vec![Value::U8(1)]),
            &Type::Value,
            &defs
        )
        .is_ok());
    }

    #[test]
    fn test_validate_tuple() {
        let defs = vec![];
        let ty = Type::Tuple(vec![Type::String, Type::U32]);
        let val = Value::Tuple(vec![Value::String("a".into()), Value::U32(1)]);
        assert!(validate_value_in_type_space(&val, &ty, &defs).is_ok());

        // Wrong arity
        let val_short = Value::Tuple(vec![Value::String("a".into())]);
        assert!(validate_value_in_type_space(&val_short, &ty, &defs).is_err());

        // Wrong inner type
        let val_bad = Value::Tuple(vec![Value::String("a".into()), Value::Bool(true)]);
        assert!(validate_value_in_type_space(&val_bad, &ty, &defs).is_err());
    }

    #[test]
    fn test_validate_record() {
        let defs = vec![TypeDef::Record {
            name: "my-state".into(),
            fields: vec![
                Field::new("count", Type::S32),
                Field::new("name", Type::String),
            ],
        }];
        let ty = Type::Ref(TypePath::simple("my-state"));

        // Valid
        let val = Value::Record {
            type_name: "my-state".into(),
            fields: vec![
                ("count".into(), Value::S32(5)),
                ("name".into(), Value::String("test".into())),
            ],
        };
        assert!(validate_value_in_type_space(&val, &ty, &defs).is_ok());

        // Missing field
        let val_missing = Value::Record {
            type_name: "my-state".into(),
            fields: vec![("count".into(), Value::S32(5))],
        };
        assert!(validate_value_in_type_space(&val_missing, &ty, &defs).is_err());

        // Extra field
        let val_extra = Value::Record {
            type_name: "my-state".into(),
            fields: vec![
                ("count".into(), Value::S32(5)),
                ("name".into(), Value::String("test".into())),
                ("bonus".into(), Value::Bool(true)),
            ],
        };
        assert!(validate_value_in_type_space(&val_extra, &ty, &defs).is_err());

        // Wrong field type
        let val_wrong_type = Value::Record {
            type_name: "my-state".into(),
            fields: vec![
                ("count".into(), Value::String("not a number".into())),
                ("name".into(), Value::String("test".into())),
            ],
        };
        assert!(validate_value_in_type_space(&val_wrong_type, &ty, &defs).is_err());
    }

    #[test]
    fn test_validate_variant() {
        let defs = vec![TypeDef::Variant {
            name: "result".into(),
            cases: vec![
                Case::new("ok", Type::String),
                Case::new("err", Type::String),
            ],
        }];
        let ty = Type::Ref(TypePath::simple("result"));

        // Valid case
        let val = Value::Variant {
            type_name: "result".into(),
            case_name: "ok".into(),
            tag: 0,
            payload: vec![Value::String("success".into())],
        };
        assert!(validate_value_in_type_space(&val, &ty, &defs).is_ok());

        // Unknown case
        let val_bad = Value::Variant {
            type_name: "result".into(),
            case_name: "maybe".into(),
            tag: 2,
            payload: vec![],
        };
        assert!(validate_value_in_type_space(&val_bad, &ty, &defs).is_err());

        // Wrong payload type
        let val_wrong = Value::Variant {
            type_name: "result".into(),
            case_name: "ok".into(),
            tag: 0,
            payload: vec![Value::U32(42)],
        };
        assert!(validate_value_in_type_space(&val_wrong, &ty, &defs).is_err());
    }

    #[test]
    fn test_validate_enum() {
        let defs = vec![TypeDef::Enum {
            name: "color".into(),
            cases: vec!["red".into(), "green".into(), "blue".into()],
        }];
        let ty = Type::Ref(TypePath::simple("color"));

        let val = Value::Variant {
            type_name: "color".into(),
            case_name: "red".into(),
            tag: 0,
            payload: vec![],
        };
        assert!(validate_value_in_type_space(&val, &ty, &defs).is_ok());

        // Unknown case
        let val_bad = Value::Variant {
            type_name: "color".into(),
            case_name: "purple".into(),
            tag: 3,
            payload: vec![],
        };
        assert!(validate_value_in_type_space(&val_bad, &ty, &defs).is_err());
    }

    #[test]
    fn test_validate_nested_record_with_variant() {
        let defs = vec![
            TypeDef::Record {
                name: "state".into(),
                fields: vec![
                    Field::new("status", Type::Ref(TypePath::simple("status"))),
                    Field::new("count", Type::U32),
                ],
            },
            TypeDef::Variant {
                name: "status".into(),
                cases: vec![Case::unit("pending"), Case::new("active", Type::String)],
            },
        ];
        let ty = Type::Ref(TypePath::simple("state"));

        // Valid: record with variant field
        let val = Value::Record {
            type_name: "state".into(),
            fields: vec![
                (
                    "status".into(),
                    Value::Variant {
                        type_name: "status".into(),
                        case_name: "active".into(),
                        tag: 1,
                        payload: vec![Value::String("running".into())],
                    },
                ),
                ("count".into(), Value::U32(10)),
            ],
        };
        assert!(validate_value_in_type_space(&val, &ty, &defs).is_ok());

        // Invalid: variant case has wrong payload
        let val_bad = Value::Record {
            type_name: "state".into(),
            fields: vec![
                (
                    "status".into(),
                    Value::Variant {
                        type_name: "status".into(),
                        case_name: "active".into(),
                        tag: 1,
                        payload: vec![Value::U32(42)], // should be String
                    },
                ),
                ("count".into(), Value::U32(10)),
            ],
        };
        assert!(validate_value_in_type_space(&val_bad, &ty, &defs).is_err());
    }

    #[test]
    fn test_validate_unresolved_ref() {
        let defs = vec![];
        let ty = Type::Ref(TypePath::simple("nonexistent"));
        let val = Value::Bool(true);
        let err = validate_value_in_type_space(&val, &ty, &defs).unwrap_err();
        assert!(matches!(err, TypeValidationError::UnresolvedRef { .. }));
    }

    #[test]
    fn test_validate_option_and_list() {
        use crate::abi::ValueType;
        let defs = vec![];

        // Option<string> with Some
        let ty = Type::Option(Box::new(Type::String));
        let val = Value::Option {
            inner_type: ValueType::String,
            value: Some(Box::new(Value::String("hi".into()))),
        };
        assert!(validate_value_in_type_space(&val, &ty, &defs).is_ok());

        // Option<string> with None
        let val_none = Value::Option {
            inner_type: ValueType::String,
            value: None,
        };
        assert!(validate_value_in_type_space(&val_none, &ty, &defs).is_ok());

        // List<u32>
        let ty_list = Type::List(Box::new(Type::U32));
        let val_list = Value::List {
            elem_type: ValueType::U32,
            items: vec![Value::U32(1), Value::U32(2)],
        };
        assert!(validate_value_in_type_space(&val_list, &ty_list, &defs).is_ok());

        // List<u32> with wrong element
        let val_bad_list = Value::List {
            elem_type: ValueType::U32,
            items: vec![Value::U32(1), Value::String("oops".into())],
        };
        assert!(validate_value_in_type_space(&val_bad_list, &ty_list, &defs).is_err());
    }

    #[test]
    fn test_hash_type_ref_resolves_structurally() {
        // A ref to a known record hashes the same as the record's structure.
        let types = vec![TypeDef::record(
            "point",
            vec![Field::new("x", Type::S32), Field::new("y", Type::S32)],
        )];

        let ref_hash = hash_type_in(&Type::named("point"), &types);
        let structural_hash =
            hash_record(&[("x", hash_type(&Type::S32)), ("y", hash_type(&Type::S32))]);
        assert_eq!(ref_hash, structural_hash);
    }

    #[test]
    fn test_hash_type_ref_unresolved_falls_back_to_path() {
        // Without typedefs in scope, a ref hashes by path (legacy behavior).
        let h1 = hash_type_in(&Type::named("unknown-type"), &[]);
        let h2 = hash_type(&Type::named("unknown-type"));
        assert_eq!(h1, h2);
        // And it's distinct from any primitive.
        assert_ne!(h1, HASH_STRING);
    }

    #[test]
    fn test_hash_type_recursive_ref_terminates() {
        // record tree { children: list<tree> } — recursive without an explicit self-ref.
        let types = vec![TypeDef::record(
            "tree",
            vec![Field::new("children", Type::list(Type::named("tree")))],
        )];

        // Should terminate and produce a stable hash.
        let h = hash_type_in(&Type::named("tree"), &types);
        let again = hash_type_in(&Type::named("tree"), &types);
        assert_eq!(h, again);

        // The inner `list<tree>` recurses into the in-progress name and returns
        // HASH_SELF_REF, equivalent to `list<self>` inside the record body.
        let expected = hash_record(&[("children", hash_list(&HASH_SELF_REF))]);
        assert_eq!(h, expected);
    }

    #[test]
    fn test_compute_interface_hash_resolves_record_refs() {
        // An interface with a record + a function referencing it should hash
        // identically whether we reference the record by name or inline its
        // structure into the function signature.
        let mut by_ref = Arena::new("test:api/podman");
        by_ref.add_type(TypeDef::record(
            "container-spec",
            vec![
                Field::new("image", Type::String),
                Field::new("name", Type::String),
            ],
        ));
        by_ref.add_function(Function::with_signature(
            "run",
            vec![Param::new("spec", Type::named("container-spec"))],
            vec![Type::result(Type::String, Type::String)],
        ));

        // Equivalent interface with the record inlined as a tuple (or hashed
        // structurally directly). We construct the expected hash by hand using
        // the structural hash primitives.
        let record_hash = hash_record(&[("image", HASH_STRING), ("name", HASH_STRING)]);
        let func_hash = hash_function(&[record_hash], &[hash_result(&HASH_STRING, &HASH_STRING)]);
        let expected = hash_interface(
            "test:api/podman",
            &[],
            &[Binding {
                name: "run",
                hash: func_hash,
            }],
        );

        assert_eq!(compute_interface_hash(&by_ref), expected);
    }
}
