//! Synor Smart Contract SDK
//!
//! This SDK provides everything needed to write smart contracts for Synor.
//! Contracts are written in Rust and compiled to WebAssembly.
//!
//! # Quick Start
//!
//! ```rust,ignore
//! use synor_sdk::prelude::*;
//!
//! #[synor_contract]
//! mod my_contract {
//!     use super::*;
//!
//!     pub fn init(_params: &[u8]) -> Result<(), Error> {
//!         Ok(())
//!     }
//!
//!     pub fn get_value() -> u64 {
//!         storage::get::<u64>(b"value").unwrap_or(0)
//!     }
//!
//!     pub fn set_value(value: u64) {
//!         storage::set(b"value", &value);
//!     }
//! }
//! ```
//!
//! # Host Functions
//!
//! The SDK provides safe wrappers for all Synor VM host functions:
//!
//! - **Storage**: Persistent key-value storage for contract state
//! - **Context**: Access to caller, block info, and transaction data
//! - **Crypto**: Hash functions (Blake3, SHA3) and signature verification
//! - **Events**: Emit indexed logs for off-chain indexing
//! - **Calls**: Cross-contract calls and delegatecall
//!
//! # Building Contracts
//!
//! ```bash
//! cargo build --target wasm32-unknown-unknown --release
//! ```

#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;

// Global allocator for WASM targets
#[cfg(all(target_arch = "wasm32", not(feature = "std")))]
mod allocator {
    use core::alloc::{GlobalAlloc, Layout};
    use core::cell::UnsafeCell;

    // Simple bump allocator for WASM
    const HEAP_SIZE: usize = 1024 * 1024; // 1 MB heap

    struct BumpAllocator {
        heap: UnsafeCell<[u8; HEAP_SIZE]>,
        offset: UnsafeCell<usize>,
    }

    unsafe impl Sync for BumpAllocator {}

    unsafe impl GlobalAlloc for BumpAllocator {
        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
            let offset = &mut *self.offset.get();
            let align = layout.align();
            let size = layout.size();

            // Align up
            let aligned = (*offset + align - 1) & !(align - 1);
            let new_offset = aligned + size;

            if new_offset > HEAP_SIZE {
                core::ptr::null_mut()
            } else {
                *offset = new_offset;
                (*self.heap.get()).as_mut_ptr().add(aligned)
            }
        }

        unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
            // Bump allocator doesn't support dealloc
        }
    }

    #[global_allocator]
    static ALLOCATOR: BumpAllocator = BumpAllocator {
        heap: UnsafeCell::new([0u8; HEAP_SIZE]),
        offset: UnsafeCell::new(0),
    };
}

// Panic handler for WASM targets
#[cfg(all(target_arch = "wasm32", not(feature = "std")))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    crate::host::revert("panic occurred");
}

pub mod abi;
pub mod context;
pub mod crypto;
pub mod error;
pub mod events;
pub mod host;
pub mod storage;
pub mod types;

// Re-exports for convenience
pub use error::{Error, Result};

/// Prelude for contract development
pub mod prelude {
    pub use crate::abi::{Decode, Encode};
    pub use crate::context::{
        block_hash, block_height, caller, chain_id, gas_remaining, origin, self_address, timestamp,
        value,
    };
    pub use crate::crypto::{blake3_hash, sha3_hash, verify_ed25519};
    pub use crate::error::{Error, Result};
    pub use crate::events::{emit, emit_raw, Event, Topic};
    pub use crate::storage;
    pub use crate::types::{Address, Hash256};

    pub use alloc::string::String;
    pub use alloc::vec;
    pub use alloc::vec::Vec;
}

/// Contract entry point macro.
///
/// This macro generates the WASM exports required by Synor VM.
///
/// # Example
///
/// ```rust,ignore
/// synor_sdk::entry_point!(init, call);
///
/// fn init(params: &[u8]) -> synor_sdk::Result<()> {
///     Ok(())
/// }
///
/// fn call(method: &str, params: &[u8]) -> synor_sdk::Result<Vec<u8>> {
///     match method {
///         "get" => Ok(storage::get_raw(b"key").unwrap_or_default()),
///         _ => Err(synor_sdk::Error::InvalidMethod),
///     }
/// }
/// ```
#[macro_export]
macro_rules! entry_point {
    ($init:ident, $call:ident) => {
        #[no_mangle]
        pub extern "C" fn __synor_init(params_ptr: i32, params_len: i32) -> i32 {
            use alloc::string::ToString;
            let params = unsafe {
                core::slice::from_raw_parts(params_ptr as *const u8, params_len as usize)
            };

            match $init(params) {
                Ok(()) => 0,
                Err(e) => {
                    $crate::host::revert(&e.to_string());
                }
            }
        }

        #[no_mangle]
        pub extern "C" fn __synor_call(args_ptr: i32, args_len: i32) -> i32 {
            use alloc::string::ToString;
            let args =
                unsafe { core::slice::from_raw_parts(args_ptr as *const u8, args_len as usize) };

            // First 4 bytes are method selector (hash of method name)
            if args.len() < 4 {
                $crate::host::revert("Invalid call data");
            }

            let method_selector = &args[..4];
            let params = &args[4..];

            match $call(method_selector, params) {
                Ok(data) => {
                    $crate::host::return_data(&data);
                    data.len() as i32
                }
                Err(e) => $crate::host::revert(&e.to_string()),
            }
        }
    };
}

/// Simplified entry point for contracts that dispatch by method name.
///
/// # Example
///
/// ```rust,ignore
/// synor_sdk::dispatch! {
///     "transfer" => transfer(to: Address, amount: u64),
///     "balance_of" => balance_of(owner: Address) -> u64,
/// }
/// ```
#[macro_export]
macro_rules! dispatch {
    (
        init => $init_fn:ident,
        $($method:literal => $fn:ident($($arg:ident: $ty:ty),*) $(-> $ret:ty)?),* $(,)?
    ) => {
        $crate::entry_point!(__dispatch_init, __dispatch_call);

        fn __dispatch_init(params: &[u8]) -> $crate::Result<()> {
            $init_fn(params)
        }

        fn __dispatch_call(selector: &[u8], params: &[u8]) -> $crate::Result<alloc::vec::Vec<u8>> {
            $(
                if selector == &$crate::abi::method_selector($method) {
                    let ($($arg,)*) = $crate::abi::decode_args::<($($ty,)*)>(params)?;
                    let result = $fn($($arg),*);
                    return $crate::abi::encode_result(&result);
                }
            )*
            Err($crate::Error::InvalidMethod)
        }
    };
}

/// Computes a method selector (first 4 bytes of blake3 hash).
pub fn method_selector(name: &str) -> [u8; 4] {
    let hash = blake3::hash(name.as_bytes());
    let bytes = hash.as_bytes();
    [bytes[0], bytes[1], bytes[2], bytes[3]]
}

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

    #[test]
    fn test_method_selector() {
        let sel = method_selector("transfer");
        assert_eq!(sel.len(), 4);
        assert_ne!(sel, [0, 0, 0, 0]);

        // Same input = same selector
        assert_eq!(sel, method_selector("transfer"));

        // Different input = different selector
        assert_ne!(sel, method_selector("mint"));
    }
}
