use core::fmt::{Debug, Display, Error as FmtError, Formatter};
use crossbeam_channel as channel;
use tracing::Span;
use ibc_proto::ibc::apps::fee::v1::{
    QueryIncentivizedPacketRequest, QueryIncentivizedPacketResponse,
};
use ibc_relayer_types::{
    applications::ics31_icq::response::CrossChainQueryResponse,
    core::{
        ics02_client::events::UpdateClient,
        ics03_connection::connection::{ConnectionEnd, IdentifiedConnectionEnd},
        ics03_connection::version::Version,
        ics04_channel::channel::{ChannelEnd, IdentifiedChannelEnd},
        ics04_channel::packet::{PacketMsgType, Sequence},
        ics23_commitment::{commitment::CommitmentPrefix, merkle::MerkleProof},
        ics24_host::identifier::ChainId,
        ics24_host::identifier::ChannelId,
        ics24_host::identifier::{ClientId, ConnectionId, PortId},
    },
    proofs::Proofs,
    signer::Signer,
    Height,
};
use crate::{
    account::Balance,
    chain::{client::ClientSettings, endpoint::ChainStatus, requests::*, tracking::TrackedMsgs},
    client_state::{AnyClientState, IdentifiedAnyClientState},
    config::ChainConfig,
    connection::ConnectionMsgType,
    consensus_state::AnyConsensusState,
    denom::DenomTrace,
    error::Error,
    event::IbcEventWithHeight,
    keyring::AnySigningKeyPair,
    light_client::AnyHeader,
    misbehaviour::MisbehaviourEvidence,
};
use super::{reply_channel, ChainHandle, ChainRequest, HealthCheck, ReplyTo, Subscription};
#[derive(Debug, Clone)]
pub struct BaseChainHandle {
    chain_id: ChainId,
    runtime_sender: channel::Sender<(Span, ChainRequest)>,
}
impl BaseChainHandle {
    pub fn new(chain_id: ChainId, sender: channel::Sender<(Span, ChainRequest)>) -> Self {
        Self {
            chain_id,
            runtime_sender: sender,
        }
    }
    fn send<F, O>(&self, f: F) -> Result<O, Error>
    where
        F: FnOnce(ReplyTo<O>) -> ChainRequest,
        O: Debug,
    {
        let (sender, receiver) = reply_channel();
        let span = Span::current();
        let input = f(sender);
        self.runtime_sender
            .send((span, input))
            .map_err(Error::send)?;
        receiver.recv().map_err(Error::channel_receive)?
    }
}
impl Display for BaseChainHandle {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
        write!(f, "BaseChainHandle {{ chain_id: {} }}", self.chain_id)
    }
}
impl ChainHandle for BaseChainHandle {
    fn new(chain_id: ChainId, sender: channel::Sender<(Span, ChainRequest)>) -> Self {
        Self::new(chain_id, sender)
    }
    fn id(&self) -> ChainId {
        self.chain_id.clone()
    }
    fn health_check(&self) -> Result<HealthCheck, Error> {
        self.send(|reply_to| ChainRequest::HealthCheck { reply_to })
    }
    fn shutdown(&self) -> Result<(), Error> {
        self.send(|reply_to| ChainRequest::Shutdown { reply_to })
    }
    fn subscribe(&self) -> Result<Subscription, Error> {
        self.send(|reply_to| ChainRequest::Subscribe { reply_to })
    }
    fn send_messages_and_wait_commit(
        &self,
        tracked_msgs: TrackedMsgs,
    ) -> Result<Vec<IbcEventWithHeight>, Error> {
        self.send(|reply_to| ChainRequest::SendMessagesAndWaitCommit {
            tracked_msgs,
            reply_to,
        })
    }
    fn send_messages_and_wait_check_tx(
        &self,
        tracked_msgs: TrackedMsgs,
    ) -> Result<Vec<tendermint_rpc::endpoint::broadcast::tx_sync::Response>, Error> {
        self.send(|reply_to| ChainRequest::SendMessagesAndWaitCheckTx {
            tracked_msgs,
            reply_to,
        })
    }
    fn get_signer(&self) -> Result<Signer, Error> {
        self.send(|reply_to| ChainRequest::Signer { reply_to })
    }
    fn config(&self) -> Result<ChainConfig, Error> {
        self.send(|reply_to| ChainRequest::Config { reply_to })
    }
    fn get_key(&self) -> Result<AnySigningKeyPair, Error> {
        self.send(|reply_to| ChainRequest::GetKey { reply_to })
    }
    fn add_key(&self, key_name: String, key: AnySigningKeyPair) -> Result<(), Error> {
        self.send(|reply_to| ChainRequest::AddKey {
            key_name,
            key,
            reply_to,
        })
    }
    fn ibc_version(&self) -> Result<Option<semver::Version>, Error> {
        self.send(|reply_to| ChainRequest::IbcVersion { reply_to })
    }
    fn query_balance(
        &self,
        key_name: Option<String>,
        denom: Option<String>,
    ) -> Result<Balance, Error> {
        self.send(|reply_to| ChainRequest::QueryBalance {
            key_name,
            denom,
            reply_to,
        })
    }
    fn query_all_balances(&self, key_name: Option<String>) -> Result<Vec<Balance>, Error> {
        self.send(|reply_to| ChainRequest::QueryAllBalances { key_name, reply_to })
    }
    fn query_denom_trace(&self, hash: String) -> Result<DenomTrace, Error> {
        self.send(|reply_to| ChainRequest::QueryDenomTrace { hash, reply_to })
    }
    fn query_application_status(&self) -> Result<ChainStatus, Error> {
        self.send(|reply_to| ChainRequest::QueryApplicationStatus { reply_to })
    }
    fn query_clients(
        &self,
        request: QueryClientStatesRequest,
    ) -> Result<Vec<IdentifiedAnyClientState>, Error> {
        self.send(|reply_to| ChainRequest::QueryClients { request, reply_to })
    }
    fn query_client_state(
        &self,
        request: QueryClientStateRequest,
        include_proof: IncludeProof,
    ) -> Result<(AnyClientState, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryClientState {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_client_connections(
        &self,
        request: QueryClientConnectionsRequest,
    ) -> Result<Vec<ConnectionId>, Error> {
        self.send(|reply_to| ChainRequest::QueryClientConnections { request, reply_to })
    }
    fn query_consensus_state_heights(
        &self,
        request: QueryConsensusStateHeightsRequest,
    ) -> Result<Vec<Height>, Error> {
        self.send(|reply_to| ChainRequest::QueryConsensusStateHeights { request, reply_to })
    }
    fn query_consensus_state(
        &self,
        request: QueryConsensusStateRequest,
        include_proof: IncludeProof,
    ) -> Result<(AnyConsensusState, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryConsensusState {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_upgraded_client_state(
        &self,
        request: QueryUpgradedClientStateRequest,
    ) -> Result<(AnyClientState, MerkleProof), Error> {
        self.send(|reply_to| ChainRequest::QueryUpgradedClientState { request, reply_to })
    }
    fn query_upgraded_consensus_state(
        &self,
        request: QueryUpgradedConsensusStateRequest,
    ) -> Result<(AnyConsensusState, MerkleProof), Error> {
        self.send(|reply_to| ChainRequest::QueryUpgradedConsensusState { request, reply_to })
    }
    fn query_commitment_prefix(&self) -> Result<CommitmentPrefix, Error> {
        self.send(|reply_to| ChainRequest::QueryCommitmentPrefix { reply_to })
    }
    fn query_compatible_versions(&self) -> Result<Vec<Version>, Error> {
        self.send(|reply_to| ChainRequest::QueryCompatibleVersions { reply_to })
    }
    fn query_connection(
        &self,
        request: QueryConnectionRequest,
        include_proof: IncludeProof,
    ) -> Result<(ConnectionEnd, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryConnection {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_connections(
        &self,
        request: QueryConnectionsRequest,
    ) -> Result<Vec<IdentifiedConnectionEnd>, Error> {
        self.send(|reply_to| ChainRequest::QueryConnections { request, reply_to })
    }
    fn query_connection_channels(
        &self,
        request: QueryConnectionChannelsRequest,
    ) -> Result<Vec<IdentifiedChannelEnd>, Error> {
        self.send(|reply_to| ChainRequest::QueryConnectionChannels { request, reply_to })
    }
    fn query_next_sequence_receive(
        &self,
        request: QueryNextSequenceReceiveRequest,
        include_proof: IncludeProof,
    ) -> Result<(Sequence, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryNextSequenceReceive {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_channels(
        &self,
        request: QueryChannelsRequest,
    ) -> Result<Vec<IdentifiedChannelEnd>, Error> {
        self.send(|reply_to| ChainRequest::QueryChannels { request, reply_to })
    }
    fn query_channel(
        &self,
        request: QueryChannelRequest,
        include_proof: IncludeProof,
    ) -> Result<(ChannelEnd, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryChannel {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_channel_client_state(
        &self,
        request: QueryChannelClientStateRequest,
    ) -> Result<Option<IdentifiedAnyClientState>, Error> {
        self.send(|reply_to| ChainRequest::QueryChannelClientState { request, reply_to })
    }
    fn build_header(
        &self,
        trusted_height: Height,
        target_height: Height,
        client_state: AnyClientState,
    ) -> Result<(AnyHeader, Vec<AnyHeader>), Error> {
        self.send(|reply_to| ChainRequest::BuildHeader {
            trusted_height,
            target_height,
            client_state,
            reply_to,
        })
    }
    fn build_client_state(
        &self,
        height: Height,
        settings: ClientSettings,
    ) -> Result<AnyClientState, Error> {
        self.send(|reply_to| ChainRequest::BuildClientState {
            height,
            settings,
            reply_to,
        })
    }
    fn build_consensus_state(
        &self,
        trusted: Height,
        target: Height,
        client_state: AnyClientState,
    ) -> Result<AnyConsensusState, Error> {
        self.send(|reply_to| ChainRequest::BuildConsensusState {
            trusted,
            target,
            client_state,
            reply_to,
        })
    }
    fn check_misbehaviour(
        &self,
        update_event: UpdateClient,
        client_state: AnyClientState,
    ) -> Result<Option<MisbehaviourEvidence>, Error> {
        self.send(|reply_to| ChainRequest::BuildMisbehaviour {
            client_state,
            update_event,
            reply_to,
        })
    }
    fn build_connection_proofs_and_client_state(
        &self,
        message_type: ConnectionMsgType,
        connection_id: &ConnectionId,
        client_id: &ClientId,
        height: Height,
    ) -> Result<(Option<AnyClientState>, Proofs), Error> {
        self.send(
            |reply_to| ChainRequest::BuildConnectionProofsAndClientState {
                message_type,
                connection_id: connection_id.clone(),
                client_id: client_id.clone(),
                height,
                reply_to,
            },
        )
    }
    fn build_channel_proofs(
        &self,
        port_id: &PortId,
        channel_id: &ChannelId,
        height: Height,
    ) -> Result<Proofs, Error> {
        self.send(|reply_to| ChainRequest::BuildChannelProofs {
            port_id: port_id.clone(),
            channel_id: channel_id.clone(),
            height,
            reply_to,
        })
    }
    fn build_packet_proofs(
        &self,
        packet_type: PacketMsgType,
        port_id: &PortId,
        channel_id: &ChannelId,
        sequence: Sequence,
        height: Height,
    ) -> Result<Proofs, Error> {
        self.send(|reply_to| ChainRequest::BuildPacketProofs {
            packet_type,
            port_id: port_id.clone(),
            channel_id: channel_id.clone(),
            sequence,
            height,
            reply_to,
        })
    }
    fn query_packet_commitment(
        &self,
        request: QueryPacketCommitmentRequest,
        include_proof: IncludeProof,
    ) -> Result<(Vec<u8>, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryPacketCommitment {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_packet_commitments(
        &self,
        request: QueryPacketCommitmentsRequest,
    ) -> Result<(Vec<Sequence>, Height), Error> {
        self.send(|reply_to| ChainRequest::QueryPacketCommitments { request, reply_to })
    }
    fn query_packet_receipt(
        &self,
        request: QueryPacketReceiptRequest,
        include_proof: IncludeProof,
    ) -> Result<(Vec<u8>, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryPacketReceipt {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_unreceived_packets(
        &self,
        request: QueryUnreceivedPacketsRequest,
    ) -> Result<Vec<Sequence>, Error> {
        self.send(|reply_to| ChainRequest::QueryUnreceivedPackets { request, reply_to })
    }
    fn query_packet_acknowledgement(
        &self,
        request: QueryPacketAcknowledgementRequest,
        include_proof: IncludeProof,
    ) -> Result<(Vec<u8>, Option<MerkleProof>), Error> {
        self.send(|reply_to| ChainRequest::QueryPacketAcknowledgement {
            request,
            include_proof,
            reply_to,
        })
    }
    fn query_packet_acknowledgements(
        &self,
        request: QueryPacketAcknowledgementsRequest,
    ) -> Result<(Vec<Sequence>, Height), Error> {
        self.send(|reply_to| ChainRequest::QueryPacketAcknowledgements { request, reply_to })
    }
    fn query_unreceived_acknowledgements(
        &self,
        request: QueryUnreceivedAcksRequest,
    ) -> Result<Vec<Sequence>, Error> {
        self.send(|reply_to| ChainRequest::QueryUnreceivedAcknowledgement { request, reply_to })
    }
    fn query_txs(&self, request: QueryTxRequest) -> Result<Vec<IbcEventWithHeight>, Error> {
        self.send(|reply_to| ChainRequest::QueryPacketEventDataFromTxs { request, reply_to })
    }
    fn query_packet_events(
        &self,
        request: QueryPacketEventDataRequest,
    ) -> Result<Vec<IbcEventWithHeight>, Error> {
        self.send(|reply_to| ChainRequest::QueryPacketEventData { request, reply_to })
    }
    fn query_host_consensus_state(
        &self,
        request: QueryHostConsensusStateRequest,
    ) -> Result<AnyConsensusState, Error> {
        self.send(|reply_to| ChainRequest::QueryHostConsensusState { request, reply_to })
    }
    fn maybe_register_counterparty_payee(
        &self,
        channel_id: ChannelId,
        port_id: PortId,
        counterparty_payee: Signer,
    ) -> Result<(), Error> {
        self.send(|reply_to| ChainRequest::MaybeRegisterCounterpartyPayee {
            channel_id,
            port_id,
            counterparty_payee,
            reply_to,
        })
    }
    fn cross_chain_query(
        &self,
        request: Vec<CrossChainQueryRequest>,
    ) -> Result<Vec<CrossChainQueryResponse>, Error> {
        self.send(|reply_to| ChainRequest::CrossChainQuery { request, reply_to })
    }
    fn query_incentivized_packet(
        &self,
        request: QueryIncentivizedPacketRequest,
    ) -> Result<QueryIncentivizedPacketResponse, Error> {
        self.send(|reply_to| ChainRequest::QueryIncentivizedPacket { request, reply_to })
    }
}