//! Event emission for contracts.
//!
//! Events (logs) are a way to emit data that can be indexed off-chain
//! but is not stored on-chain. They're useful for:
//! - Tracking token transfers
//! - Recording state changes
//! - Debugging and monitoring
//!
//! # Example
//!
//! ```rust,ignore
//! use synor_sdk::events::{emit, Event, Topic};
//!
//! // Define an event
//! struct Transfer {
//!     from: Address,
//!     to: Address,
//!     amount: u64,
//! }
//!
//! impl Event for Transfer {
//!     fn topics(&self) -> Vec<Topic> {
//!         vec![
//!             Topic::from(b"Transfer"),
//!             Topic::from(&self.from),
//!             Topic::from(&self.to),
//!         ]
//!     }
//!
//!     fn data(&self) -> Vec<u8> {
//!         self.amount.to_le_bytes().to_vec()
//!     }
//! }
//!
//! // Emit the event
//! emit(&Transfer { from, to, amount });
//! ```

use alloc::vec::Vec;

use crate::host;
use crate::types::{Address, Hash256};

/// A topic (indexed field) in an event log.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Topic(pub [u8; 32]);

impl Topic {
    /// Creates a topic from raw bytes.
    pub fn new(bytes: [u8; 32]) -> Self {
        Topic(bytes)
    }

    /// Creates a topic from a hash.
    pub fn from_hash(hash: Hash256) -> Self {
        Topic(hash.0)
    }

    /// Creates a topic from a string (hashed).
    pub fn from_name(name: &str) -> Self {
        Topic(host::blake3(name.as_bytes()))
    }

    /// Creates a topic from bytes (hashed if longer than 32 bytes).
    pub fn from_bytes(bytes: &[u8]) -> Self {
        if bytes.len() <= 32 {
            let mut arr = [0u8; 32];
            arr[..bytes.len()].copy_from_slice(bytes);
            Topic(arr)
        } else {
            Topic(host::blake3(bytes))
        }
    }

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

impl From<[u8; 32]> for Topic {
    fn from(bytes: [u8; 32]) -> Self {
        Topic(bytes)
    }
}

impl From<Hash256> for Topic {
    fn from(hash: Hash256) -> Self {
        Topic(hash.0)
    }
}

impl From<&Address> for Topic {
    fn from(addr: &Address) -> Self {
        // Use first 32 bytes of address
        let mut arr = [0u8; 32];
        arr.copy_from_slice(&addr.0[..32]);
        Topic(arr)
    }
}

impl From<&str> for Topic {
    fn from(name: &str) -> Self {
        Topic::from_name(name)
    }
}

impl From<u64> for Topic {
    fn from(value: u64) -> Self {
        let mut arr = [0u8; 32];
        arr[24..32].copy_from_slice(&value.to_be_bytes());
        Topic(arr)
    }
}

impl From<u128> for Topic {
    fn from(value: u128) -> Self {
        let mut arr = [0u8; 32];
        arr[16..32].copy_from_slice(&value.to_be_bytes());
        Topic(arr)
    }
}

/// Trait for types that can be emitted as events.
pub trait Event {
    /// Returns the indexed topics for this event.
    ///
    /// The first topic is typically the event signature (hash of event name).
    /// Maximum 4 topics are allowed.
    fn topics(&self) -> Vec<Topic>;

    /// Returns the non-indexed data for this event.
    fn data(&self) -> Vec<u8>;
}

/// Emits an event.
pub fn emit<E: Event>(event: &E) {
    let topics = event.topics();
    let data = event.data();

    let topic_bytes: Vec<[u8; 32]> = topics.iter().map(|t| t.0).collect();
    host::emit_log(&topic_bytes, &data);
}

/// Emits a raw event with topics and data.
pub fn emit_raw(topics: &[[u8; 32]], data: &[u8]) {
    host::emit_log(topics, data);
}

/// Helper macro for defining events.
///
/// # Example
///
/// ```rust,ignore
/// synor_sdk::define_event! {
///     /// Transfer event
///     Transfer {
///         #[indexed] from: Address,
///         #[indexed] to: Address,
///         amount: u64,
///     }
/// }
/// ```
#[macro_export]
macro_rules! define_event {
    (
        $(#[$meta:meta])*
        $name:ident {
            $(
                $(#[indexed])?
                $field:ident: $ty:ty
            ),* $(,)?
        }
    ) => {
        $(#[$meta])*
        #[derive(Clone, Debug)]
        pub struct $name {
            $(pub $field: $ty,)*
        }

        impl $crate::events::Event for $name {
            fn topics(&self) -> alloc::vec::Vec<$crate::events::Topic> {
                let mut topics = alloc::vec::Vec::new();
                topics.push($crate::events::Topic::from_name(stringify!($name)));
                // Add indexed fields
                topics
            }

            fn data(&self) -> alloc::vec::Vec<u8> {
                // Serialize non-indexed fields
                borsh::to_vec(self).unwrap_or_default()
            }
        }
    };
}

// Common events

/// Transfer event for token contracts.
#[derive(Clone, Debug)]
pub struct Transfer {
    pub from: Address,
    pub to: Address,
    pub amount: u64,
}

impl Event for Transfer {
    fn topics(&self) -> Vec<Topic> {
        alloc::vec![
            Topic::from_name("Transfer"),
            Topic::from(&self.from),
            Topic::from(&self.to),
        ]
    }

    fn data(&self) -> Vec<u8> {
        self.amount.to_le_bytes().to_vec()
    }
}

/// Approval event for token contracts.
#[derive(Clone, Debug)]
pub struct Approval {
    pub owner: Address,
    pub spender: Address,
    pub amount: u64,
}

impl Event for Approval {
    fn topics(&self) -> Vec<Topic> {
        alloc::vec![
            Topic::from_name("Approval"),
            Topic::from(&self.owner),
            Topic::from(&self.spender),
        ]
    }

    fn data(&self) -> Vec<u8> {
        self.amount.to_le_bytes().to_vec()
    }
}

/// Ownership transferred event.
#[derive(Clone, Debug)]
pub struct OwnershipTransferred {
    pub previous_owner: Address,
    pub new_owner: Address,
}

impl Event for OwnershipTransferred {
    fn topics(&self) -> Vec<Topic> {
        alloc::vec![
            Topic::from_name("OwnershipTransferred"),
            Topic::from(&self.previous_owner),
            Topic::from(&self.new_owner),
        ]
    }

    fn data(&self) -> Vec<u8> {
        Vec::new()
    }
}

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

    #[test]
    fn test_topic_from_name() {
        let topic = Topic::from_name("Transfer");
        assert_ne!(topic.0, [0u8; 32]);
    }

    #[test]
    fn test_topic_from_u64() {
        let topic = Topic::from(12345u64);
        // Value should be in last 8 bytes
        let value = u64::from_be_bytes(topic.0[24..32].try_into().unwrap());
        assert_eq!(value, 12345);
    }

    #[test]
    fn test_transfer_event() {
        let event = Transfer {
            from: Address::zero(),
            to: Address::zero(),
            amount: 1000,
        };

        let topics = event.topics();
        assert_eq!(topics.len(), 3);

        let data = event.data();
        assert_eq!(data.len(), 8);
    }
}
