use nym_client_core::client::base_client::storage::gateways_storage::GatewayPublishedData;
use nym_sdk::mixnet::{
    self, ed25519, ActiveGateway, BadGateway, ClientKeys, EmptyReplyStorage,
    EphemeralCredentialStorage, GatewayRegistration, GatewaysDetailsStore, KeyStore,
    MixnetClientStorage, MixnetMessageSender,
};
use nym_topology::provider_trait::async_trait;

#[tokio::main]
async fn main() {
    nym_bin_common::logging::setup_tracing_logger();

    // Just some plain data to pretend we have some external storage that the application
    // implementer is using.
    let mock_storage = MockClientStorage::empty();
    let mut client = mixnet::MixnetClientBuilder::new_with_storage(mock_storage)
        .build()
        .unwrap()
        .connect_to_mixnet()
        .await
        .unwrap();

    // Be able to get our client address
    let our_address = client.nym_address();
    println!("Our client nym address is: {our_address}");

    // Send important info up the pipe to a buddy
    client
        .send_plain_message(*our_address, "hello there")
        .await
        .unwrap();

    println!("Waiting for message");
    if let Some(received) = client.wait_for_messages().await {
        for r in received {
            println!("Received: {}", String::from_utf8_lossy(&r.message));
        }
    }

    client.disconnect().await;
}

#[derive(Clone)]
#[allow(unused)]
struct MockClientStorage {
    pub key_store: MockKeyStore,
    pub gateway_details_store: MockGatewayDetailsStore,
    pub reply_store: EmptyReplyStorage,
    pub credential_store: EphemeralCredentialStorage,
}

impl MockClientStorage {
    fn empty() -> Self {
        Self {
            key_store: MockKeyStore,
            gateway_details_store: MockGatewayDetailsStore,
            reply_store: EmptyReplyStorage::default(),
            credential_store: EphemeralCredentialStorage::default(),
        }
    }
}

impl MixnetClientStorage for MockClientStorage {
    type KeyStore = MockKeyStore;
    type ReplyStore = EmptyReplyStorage;
    type CredentialStore = EphemeralCredentialStorage;
    type GatewaysDetailsStore = MockGatewayDetailsStore;

    fn into_runtime_stores(
        self,
    ) -> (
        Self::ReplyStore,
        Self::CredentialStore,
        Self::GatewaysDetailsStore,
    ) {
        (
            self.reply_store,
            self.credential_store,
            self.gateway_details_store,
        )
    }

    fn key_store(&self) -> &Self::KeyStore {
        &self.key_store
    }

    fn reply_store(&self) -> &Self::ReplyStore {
        &self.reply_store
    }

    fn credential_store(&self) -> &Self::CredentialStore {
        &self.credential_store
    }

    fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore {
        &self.gateway_details_store
    }
}

#[derive(Clone)]
struct MockKeyStore;

#[async_trait]
impl KeyStore for MockKeyStore {
    type StorageError = MyError;

    async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
        println!("loading stored keys");

        Err(MyError)
    }

    async fn store_keys(&self, _keys: &ClientKeys) -> Result<(), Self::StorageError> {
        println!("storing keys");

        Ok(())
    }
}

#[derive(Clone)]
struct MockGatewayDetailsStore;

#[async_trait]
impl GatewaysDetailsStore for MockGatewayDetailsStore {
    type StorageError = MyError;

    async fn active_gateway(&self) -> Result<ActiveGateway, Self::StorageError> {
        println!("getting active gateway");

        Err(MyError)
    }

    async fn set_active_gateway(&self, _gateway_id: &str) -> Result<(), Self::StorageError> {
        println!("setting active gateway");

        Ok(())
    }

    async fn all_gateways(&self) -> Result<Vec<GatewayRegistration>, Self::StorageError> {
        println!("getting all registered gateways");

        Err(MyError)
    }

    async fn has_gateway_details(&self, _gateway_id: &str) -> Result<bool, Self::StorageError> {
        println!("checking for gateway details");

        Err(MyError)
    }

    async fn load_gateway_details(
        &self,
        _gateway_id: &str,
    ) -> Result<GatewayRegistration, Self::StorageError> {
        println!("loading gateway details");

        Err(MyError)
    }

    async fn store_gateway_details(
        &self,
        _details: &GatewayRegistration,
    ) -> Result<(), Self::StorageError> {
        println!("storing gateway details");

        Ok(())
    }

    async fn update_gateway_published_data(
        &self,
        _gateway_id: &ed25519::PublicKey,
        _details: &GatewayPublishedData,
    ) -> Result<(), Self::StorageError> {
        println!("updating gateway details");

        Ok(())
    }

    async fn remove_gateway_details(&self, _gateway_id: &str) -> Result<(), Self::StorageError> {
        println!("removing gateway details");

        Ok(())
    }
}

//
// struct MockReplyStore;
//
// #[async_trait]
// impl ReplyStorageBackend for MockReplyStore {
//     type StorageError = MyError;
//
//     async fn flush_surb_storage(
//         &mut self,
//         _storage: &CombinedReplyStorage,
//     ) -> Result<(), Self::StorageError> {
//         todo!()
//     }
//
//     async fn init_fresh(&mut self, _fresh: &CombinedReplyStorage) -> Result<(), Self::StorageError> {
//         todo!()
//     }
//
//     async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
//         todo!()
//     }
// }
//
// struct MockCredentialStore;
//
// #[async_trait]
// impl CredentialStorage for MockCredentialStore {
//     type StorageError = MyError;
//
//     async fn insert_coconut_credential(
//         &self,
//         _voucher_value: String,
//         _voucher_info: String,
//         _serial_number: String,
//        _binding_number: String,
//         _signature: String,
//         _epoch_id: String,
//     ) -> Result<(), Self::StorageError> {
//         todo!()
//     }
//
//     async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, Self::StorageError> {
//         todo!()
//     }
//
//     async fn consume_coconut_credential(&self, id: i64) -> Result<(), Self::StorageError> {
//         todo!()
//     }
// }

#[derive(thiserror::Error, Debug)]
#[error("foobar")]
struct MyError;

impl From<BadGateway> for MyError {
    fn from(_: BadGateway) -> Self {
        MyError
    }
}
