use eyre::eyre;
use http::Uri;
use ibc_relayer::config::default::max_grpc_decoding_size;
use prost::Message;
use ibc_proto::cosmos::gov::v1beta1::{query_client::QueryClient, QueryProposalRequest};
use ibc_proto::ibc::core::client::v1::UpgradeProposal;
use ibc_relayer::error::Error as RelayerError;
use crate::chain::cli::upgrade::vote_proposal;
use crate::chain::driver::ChainDriver;
use crate::error::Error;
use crate::prelude::handle_generic_error;
use crate::types::tagged::*;
pub trait ChainProposalMethodsExt {
    fn query_upgrade_proposal_height(
        &self,
        grpc_address: &Uri,
        proposal_id: u64,
    ) -> Result<u64, Error>;
    fn vote_proposal(&self, fees: &str) -> Result<(), Error>;
}
impl<'a, Chain: Send> ChainProposalMethodsExt for MonoTagged<Chain, &'a ChainDriver> {
    fn query_upgrade_proposal_height(
        &self,
        grpc_address: &Uri,
        proposal_id: u64,
    ) -> Result<u64, Error> {
        self.value()
            .runtime
            .block_on(query_upgrade_proposal_height(grpc_address, proposal_id))
    }
    fn vote_proposal(&self, fees: &str) -> Result<(), Error> {
        vote_proposal(
            self.value().chain_id.as_str(),
            &self.value().command_path,
            &self.value().home_path,
            &self.value().rpc_listen_address(),
            fees,
        )?;
        Ok(())
    }
}
pub async fn query_upgrade_proposal_height(
    grpc_address: &Uri,
    proposal_id: u64,
) -> Result<u64, Error> {
    let mut client = match QueryClient::connect(grpc_address.clone()).await {
        Ok(client) => client,
        Err(_) => {
            return Err(Error::query_client());
        }
    };
    client = client.max_decoding_message_size(max_grpc_decoding_size().get_bytes() as usize);
    let request = tonic::Request::new(QueryProposalRequest { proposal_id });
    let response = client
        .proposal(request)
        .await
        .map(|r| r.into_inner())
        .map_err(|e| RelayerError::grpc_status(e, "query_upgrade_proposal_height".to_owned()))?;
    let proposal = response
        .proposal
        .ok_or_else(|| RelayerError::empty_query_account(proposal_id.to_string()))?;
    let proposal_content = proposal
        .content
        .ok_or_else(|| eyre!("failed to retrieve content of Proposal"))?;
    if proposal_content.type_url != *"/ibc.core.client.v1.UpgradeProposal" {
        return Err(Error::incorrect_proposal_type_url(
            proposal_content.type_url,
        ));
    }
    let upgrade_plan =
        UpgradeProposal::decode(&proposal_content.value as &[u8]).map_err(handle_generic_error)?;
    let plan = upgrade_plan
        .plan
        .ok_or_else(|| eyre!("failed to plan from UpgradeProposal"))?;
    Ok(plan.height as u64)
}