//! WASM Virtual Machine for Synor smart contracts.
//!
//! Synor uses Rust-only smart contracts compiled to WebAssembly. This provides:
//! - Memory safety and type safety
//! - Predictable gas metering
//! - Sandboxed execution
//! - Rich standard library
//!
//! # Architecture
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────────┐
//! │                      SMART CONTRACT VM                          │
//! ├─────────────────────────────────────────────────────────────────┤
//! │                                                                  │
//! │  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐  │
//! │  │   WASM       │  │    Host      │  │     Contract         │  │
//! │  │   Engine     │  │  Functions   │  │     Storage          │  │
//! │  └──────┬───────┘  └──────┬───────┘  └──────────┬───────────┘  │
//! │         │                 │                      │              │
//! │         └─────────────────┼──────────────────────┘              │
//! │                           │                                     │
//! │                    ┌──────▼───────┐                            │
//! │                    │  Execution   │                            │
//! │                    │   Context    │                            │
//! │                    └──────────────┘                            │
//! │                                                                  │
//! └─────────────────────────────────────────────────────────────────┘
//! ```
//!
//! # Contract Interface
//!
//! Contracts must export these functions:
//! - `init(params: &[u8]) -> Result<(), Error>` - Initialize contract
//! - `call(method: &str, params: &[u8]) -> Result<Vec<u8>, Error>` - Call method

#![allow(dead_code)]

pub mod context;
pub mod engine;
pub mod gas;
pub mod gas_estimator;
pub mod host;
pub mod storage;

pub use context::{CallContext, ExecutionContext};
pub use engine::{ContractModule, VmEngine, VmInstance};
pub use gas::{Gas, GasConfig, GasMeter};
pub use gas_estimator::{costs, GasEstimate, GasEstimator};
pub use host::HostFunctions;
pub use storage::{ContractStorage, MemoryStorage, StorageKey, StorageValue};

use synor_types::{Address, Hash256};

/// Maximum contract code size (1 MB).
pub const MAX_CONTRACT_SIZE: usize = 1024 * 1024;

/// Maximum memory pages (64 KB each, max 16 MB).
pub const MAX_MEMORY_PAGES: u32 = 256;

/// Maximum call depth.
pub const MAX_CALL_DEPTH: u32 = 64;

/// Maximum execution time (milliseconds).
pub const MAX_EXECUTION_TIME_MS: u64 = 30_000;

/// Contract identifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ContractId(pub Hash256);

impl ContractId {
    /// Creates a new contract ID.
    pub fn new(hash: Hash256) -> Self {
        ContractId(hash)
    }

    /// Creates from bytes.
    pub fn from_bytes(bytes: [u8; 32]) -> Self {
        ContractId(Hash256::from_bytes(bytes))
    }

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

impl std::fmt::Display for ContractId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", hex::encode(&self.0.as_bytes()[..8]))
    }
}

/// Contract execution result.
#[derive(Clone, Debug)]
pub struct ExecutionResult {
    /// Return data.
    pub return_data: Vec<u8>,
    /// Gas used.
    pub gas_used: u64,
    /// Logs emitted.
    pub logs: Vec<ContractLog>,
    /// Storage changes.
    pub storage_changes: Vec<StorageChange>,
    /// Internal calls made.
    pub internal_calls: Vec<InternalCall>,
}

impl ExecutionResult {
    /// Creates a successful empty result.
    pub fn success() -> Self {
        ExecutionResult {
            return_data: Vec::new(),
            gas_used: 0,
            logs: Vec::new(),
            storage_changes: Vec::new(),
            internal_calls: Vec::new(),
        }
    }

    /// Creates a result with return data.
    pub fn with_data(data: Vec<u8>) -> Self {
        ExecutionResult {
            return_data: data,
            gas_used: 0,
            logs: Vec::new(),
            storage_changes: Vec::new(),
            internal_calls: Vec::new(),
        }
    }
}

/// Log emitted by a contract.
#[derive(Clone, Debug)]
pub struct ContractLog {
    /// Contract that emitted the log.
    pub contract: ContractId,
    /// Log topics (indexed).
    pub topics: Vec<Hash256>,
    /// Log data.
    pub data: Vec<u8>,
}

/// Storage change made by a contract.
#[derive(Clone, Debug)]
pub struct StorageChange {
    /// Contract storage.
    pub contract: ContractId,
    /// Storage key.
    pub key: StorageKey,
    /// Old value (None if new).
    pub old_value: Option<StorageValue>,
    /// New value (None if deleted).
    pub new_value: Option<StorageValue>,
}

/// Internal contract call.
#[derive(Clone, Debug)]
pub struct InternalCall {
    /// Caller contract.
    pub from: ContractId,
    /// Callee contract.
    pub to: ContractId,
    /// Method called.
    pub method: String,
    /// Call value.
    pub value: u64,
    /// Gas limit.
    pub gas_limit: u64,
    /// Input data.
    pub input: Vec<u8>,
    /// Success status.
    pub success: bool,
    /// Return data.
    pub return_data: Vec<u8>,
}

/// Contract deployment parameters.
#[derive(Clone, Debug)]
pub struct DeployParams {
    /// Contract bytecode (WASM).
    pub code: Vec<u8>,
    /// Constructor arguments.
    pub args: Vec<u8>,
    /// Initial value to send.
    pub value: u64,
    /// Gas limit.
    pub gas_limit: u64,
    /// Deployer address.
    pub deployer: Address,
    /// Salt for CREATE2-style addresses.
    pub salt: Option<Hash256>,
}

/// Contract call parameters.
#[derive(Clone, Debug)]
pub struct CallParams {
    /// Target contract.
    pub contract: ContractId,
    /// Method to call.
    pub method: String,
    /// Call arguments.
    pub args: Vec<u8>,
    /// Value to send.
    pub value: u64,
    /// Gas limit.
    pub gas_limit: u64,
    /// Caller address.
    pub caller: Address,
}

/// VM errors.
#[derive(Debug, Clone, thiserror::Error)]
pub enum VmError {
    /// Contract not found.
    #[error("Contract not found: {0}")]
    ContractNotFound(ContractId),

    /// Invalid bytecode.
    #[error("Invalid bytecode: {0}")]
    InvalidBytecode(String),

    /// Bytecode too large.
    #[error("Bytecode too large: {size} > {max}")]
    BytecodeTooLarge { size: usize, max: usize },

    /// Out of gas.
    #[error("Out of gas: used {used}, limit {limit}")]
    OutOfGas { used: u64, limit: u64 },

    /// Execution error.
    #[error("Execution error: {0}")]
    ExecutionError(String),

    /// Memory access violation.
    #[error("Memory access violation: {0}")]
    MemoryViolation(String),

    /// Stack overflow.
    #[error("Stack overflow")]
    StackOverflow,

    /// Call depth exceeded.
    #[error("Call depth exceeded: {0} > {}", MAX_CALL_DEPTH)]
    CallDepthExceeded(u32),

    /// Execution timeout.
    #[error("Execution timeout")]
    Timeout,

    /// Invalid method.
    #[error("Invalid method: {0}")]
    InvalidMethod(String),

    /// Serialization error.
    #[error("Serialization error: {0}")]
    SerializationError(String),

    /// Storage error.
    #[error("Storage error: {0}")]
    StorageError(String),

    /// Host function error.
    #[error("Host function error: {0}")]
    HostError(String),

    /// Contract panicked.
    #[error("Contract panicked: {0}")]
    Panic(String),

    /// Reverted by contract.
    #[error("Reverted: {0}")]
    Revert(String),
}

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

    #[test]
    fn test_contract_id() {
        let hash = Hash256::from_bytes([0x42; 32]);
        let id = ContractId::new(hash);

        assert_eq!(id.as_bytes(), &[0x42; 32]);
        assert!(!id.to_string().is_empty());
    }

    #[test]
    fn test_execution_result() {
        let result = ExecutionResult::success();
        assert!(result.return_data.is_empty());
        assert_eq!(result.gas_used, 0);

        let result = ExecutionResult::with_data(vec![1, 2, 3]);
        assert_eq!(result.return_data, vec![1, 2, 3]);
    }
}
