//! Events generated by solo machine
mod event_handler;

use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::UnboundedSender;

use crate::{
    cosmos::crypto::PublicKey,
    ibc::core::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, Identifier},
    model::ConnectionDetails,
};

pub use event_handler::*;

/// Events emitted by IBC service
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
#[allow(clippy::large_enum_variant)]
pub enum Event {
    // ----- IBC events ----- //
    /// Minted tokens on IBC enabled chain
    TokensMinted {
        /// Chain ID of IBC enabled chain
        chain_id: ChainId,
        /// Optional request ID (for tracking purposes)
        request_id: Option<String>,
        /// Address of account on IBC enabled chain
        to_address: String,
        /// Amount of tokens minted
        amount: u64,
        /// Denom of tokens minted
        denom: Identifier,
        /// Hash of transaction on IBC enabled chain (in hex)
        transaction_hash: String,
    },
    /// Burnt tokens on IBC enabled chain
    TokensBurnt {
        /// Chain ID of IBC enabled chain
        chain_id: ChainId,
        /// Optional request ID (for tracking purposes)
        request_id: Option<String>,
        /// Address of account on IBC enabled chain
        from_address: String,
        /// Amount of tokens minted
        amount: u64,
        /// Denom of tokens minted
        denom: Identifier,
        /// Hash of transaction on IBC enabled chain (in hex)
        transaction_hash: String,
    },
    /// Updated signer's public key on IBC enabled change for future messages from solo machine
    SignerUpdated {
        /// Chain ID of IBC enabled chain
        chain_id: ChainId,
        /// Old signer's public key
        old_public_key: PublicKey,
        /// New signer's public key
        new_public_key: PublicKey,
    },

    // ----- IBC connection handshake events ----- //
    /// Created solo machine client on IBC enabled chain
    CreatedSoloMachineClient {
        /// Client ID of solo machine client on IBC enabled chain
        client_id: ClientId,
    },
    /// Created tendermint client on solo machine
    CreatedTendermintClient {
        /// Client ID of IBC enabled chain on solo machine
        client_id: ClientId,
    },
    /// Initialized connection on IBC enabled chain
    InitializedConnectionOnTendermint {
        /// Connection ID of solo machine client on IBC enabled chain
        connection_id: ConnectionId,
    },
    /// Initialized connection on solo machine
    InitializedConnectionOnSoloMachine {
        /// Connection ID of IBC enabled chain on solo machine
        connection_id: ConnectionId,
    },
    /// Confirmed connection on IBC enabled chain
    ConfirmedConnectionOnTendermint {
        /// Connection ID of solo machine client on IBC enabled chain
        connection_id: ConnectionId,
    },
    /// Confirmed connection on solo machine
    ConfirmedConnectionOnSoloMachine {
        /// Connection ID of IBC enabled chain on solo machine
        connection_id: ConnectionId,
    },
    /// Initialized channel on IBC enabled chain
    InitializedChannelOnTendermint {
        /// Channel ID of solo machine client on IBC enabled chain
        channel_id: ChannelId,
    },
    /// Close channel on IBC enabled chain
    CloseChannelInitOnSoloMachine {
        /// Chain ID of IBC enabled chain
        chain_id: String,
        /// Channel ID of IBC enabled chain on solo machine
        channel_id: ChannelId,
    },
    /// Initialized channel on solo machine
    InitializedChannelOnSoloMachine {
        /// Channel ID of IBC enabled chain on solo machine
        channel_id: ChannelId,
    },
    /// Confirmed channel on IBC enabled chain
    ConfirmedChannelOnTendermint {
        /// Channel ID of solo machine client on IBC enabled chain
        channel_id: ChannelId,
    },
    /// Confirmed channel on solo machine
    ConfirmedChannelOnSoloMachine {
        /// Channel ID of IBC enabled chain on solo machine
        channel_id: ChannelId,
    },
    /// Connection successfully established
    ConnectionEstablished {
        /// Chain ID of IBC enabled chain
        chain_id: ChainId,
        /// Connection details
        connection_details: ConnectionDetails,
    },

    // ----- Chain events ----- //
    /// Added new chain metadata to solo machine
    ChainAdded {
        /// Chain ID
        chain_id: ChainId,
    },

    // ----- Other events ----- //
    /// Warning
    Warning {
        /// Warning message
        message: String,
    },
}

pub(crate) fn notify_event(notifier: &Option<UnboundedSender<Event>>, event: Event) -> Result<()> {
    match notifier {
        None => Ok(()),
        Some(ref notifier) => notifier
            .send(event)
            .map_err(|err| anyhow!("unable to send event to notifier: {}", err)),
    }
}
