//! ABI encoding and decoding for contract calls.
//!
//! Provides utilities for encoding/decoding function arguments
//! and return values using Borsh serialization.

use alloc::string::String;
use alloc::vec::Vec;
use borsh::{BorshDeserialize, BorshSerialize};

use crate::error::{Error, Result};
use crate::host;

/// Trait for types that can be ABI-encoded.
pub trait Encode {
    /// Encodes the value to bytes.
    fn encode(&self) -> Vec<u8>;
}

/// Trait for types that can be ABI-decoded.
pub trait Decode: Sized {
    /// Decodes a value from bytes.
    fn decode(data: &[u8]) -> Result<Self>;
}

// Implement for all Borsh types
impl<T: BorshSerialize> Encode for T {
    fn encode(&self) -> Vec<u8> {
        borsh::to_vec(self).unwrap_or_default()
    }
}

impl<T: BorshDeserialize> Decode for T {
    fn decode(data: &[u8]) -> Result<Self> {
        T::try_from_slice(data).map_err(|e| Error::serialization(alloc::format!("{}", e)))
    }
}

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

/// Encodes a function call with method selector and arguments.
pub fn encode_call<T: BorshSerialize>(method: &str, args: &T) -> Vec<u8> {
    let selector = method_selector(method);
    let args_data = borsh::to_vec(args).unwrap_or_default();

    let mut data = Vec::with_capacity(4 + args_data.len());
    data.extend_from_slice(&selector);
    data.extend_from_slice(&args_data);
    data
}

/// Decodes function arguments from calldata (excluding selector).
pub fn decode_args<T: BorshDeserialize>(data: &[u8]) -> Result<T> {
    T::try_from_slice(data).map_err(|e| Error::serialization(alloc::format!("{}", e)))
}

/// Encodes a function result.
pub fn encode_result<T: BorshSerialize>(result: &T) -> Result<Vec<u8>> {
    borsh::to_vec(result).map_err(|e| Error::serialization(alloc::format!("{}", e)))
}

/// Decodes a function result.
pub fn decode_result<T: BorshDeserialize>(data: &[u8]) -> Result<T> {
    T::try_from_slice(data).map_err(|e| Error::serialization(alloc::format!("{}", e)))
}

/// Decodes method selector from calldata.
pub fn decode_selector(data: &[u8]) -> Option<[u8; 4]> {
    if data.len() < 4 {
        return None;
    }
    Some([data[0], data[1], data[2], data[3]])
}

/// Checks if calldata matches a method selector.
pub fn matches_selector(data: &[u8], selector: &[u8; 4]) -> bool {
    data.len() >= 4 && &data[..4] == selector
}

/// Helper struct for building calldata.
pub struct CallBuilder {
    data: Vec<u8>,
}

impl CallBuilder {
    /// Creates a new call builder for a method.
    pub fn new(method: &str) -> Self {
        let selector = method_selector(method);
        CallBuilder {
            data: selector.to_vec(),
        }
    }

    /// Adds an argument.
    pub fn arg<T: BorshSerialize>(mut self, value: &T) -> Self {
        let encoded = borsh::to_vec(value).unwrap_or_default();
        self.data.extend_from_slice(&encoded);
        self
    }

    /// Builds the calldata.
    pub fn build(self) -> Vec<u8> {
        self.data
    }
}

/// Helper struct for decoding calldata.
pub struct CallDecoder<'a> {
    data: &'a [u8],
    offset: usize,
}

impl<'a> CallDecoder<'a> {
    /// Creates a new decoder (skipping the 4-byte selector).
    pub fn new(data: &'a [u8]) -> Result<Self> {
        if data.len() < 4 {
            return Err(Error::invalid_args("Calldata too short"));
        }
        Ok(CallDecoder { data, offset: 4 })
    }

    /// Gets the method selector.
    pub fn selector(&self) -> [u8; 4] {
        [self.data[0], self.data[1], self.data[2], self.data[3]]
    }

    /// Decodes the next argument.
    pub fn arg<T: BorshDeserialize>(&mut self) -> Result<T> {
        let value = T::try_from_slice(&self.data[self.offset..])
            .map_err(|e| Error::serialization(alloc::format!("{}", e)))?;

        // Advance offset by the size that was read
        // This is a simplification - actual implementation would track exact bytes read
        self.offset = self.data.len();

        Ok(value)
    }

    /// Returns remaining data.
    pub fn remaining(&self) -> &[u8] {
        &self.data[self.offset..]
    }
}

/// Function signature for ABI generation.
#[derive(Clone, Debug)]
pub struct FunctionSig {
    /// Function name.
    pub name: String,
    /// Input parameter types (as strings).
    pub inputs: Vec<String>,
    /// Output type (as string).
    pub output: Option<String>,
}

impl FunctionSig {
    /// Creates a new function signature.
    pub fn new(name: &str) -> Self {
        FunctionSig {
            name: String::from(name),
            inputs: Vec::new(),
            output: None,
        }
    }

    /// Adds an input parameter.
    pub fn input(mut self, ty: &str) -> Self {
        self.inputs.push(String::from(ty));
        self
    }

    /// Sets the output type.
    pub fn output(mut self, ty: &str) -> Self {
        self.output = Some(String::from(ty));
        self
    }

    /// Computes the selector for this function.
    pub fn selector(&self) -> [u8; 4] {
        method_selector(&self.name)
    }
}

/// Contract ABI description.
#[derive(Clone, Debug, Default)]
pub struct ContractAbi {
    /// Contract name.
    pub name: String,
    /// Functions.
    pub functions: Vec<FunctionSig>,
}

impl ContractAbi {
    /// Creates a new ABI.
    pub fn new(name: &str) -> Self {
        ContractAbi {
            name: String::from(name),
            functions: Vec::new(),
        }
    }

    /// Adds a function.
    pub fn function(mut self, sig: FunctionSig) -> Self {
        self.functions.push(sig);
        self
    }
}

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

    #[test]
    fn test_method_selector() {
        let sel1 = method_selector("transfer");
        let sel2 = method_selector("transfer");
        let sel3 = method_selector("approve");

        assert_eq!(sel1, sel2);
        assert_ne!(sel1, sel3);
    }

    #[test]
    fn test_encode_decode() {
        let value: u64 = 12345;
        let encoded = value.encode();
        let decoded: u64 = u64::decode(&encoded).unwrap();
        assert_eq!(value, decoded);
    }

    #[test]
    fn test_call_builder() {
        let call = CallBuilder::new("transfer")
            .arg(&[0u8; 34]) // to
            .arg(&1000u64) // amount
            .build();

        assert!(call.len() > 4);
        assert_eq!(&call[..4], &method_selector("transfer"));
    }

    #[test]
    fn test_function_sig() {
        let sig = FunctionSig::new("transfer")
            .input("Address")
            .input("u64")
            .output("bool");

        assert_eq!(sig.inputs.len(), 2);
        assert_eq!(sig.output, Some(String::from("bool")));
    }
}
