use futures::channel;
use tentacle::{
    ProtocolId, async_trait,
    builder::{MetaBuilder, ServiceBuilder},
    bytes::Bytes,
    context::{ProtocolContext, ProtocolContextMutRef},
    multiaddr::Multiaddr,
    secio::NoopKeyProvider,
    service::{ProtocolHandle, ProtocolMeta, Service, TargetProtocol},
    traits::{ServiceHandle, ServiceProtocol},
};

struct PHandle;

#[async_trait]
impl ServiceProtocol for PHandle {
    async fn init(&mut self, _context: &mut ProtocolContext) {}

    async fn connected(&mut self, context: ProtocolContextMutRef<'_>, _version: &str) {
        if context.session.ty.is_inbound() {
            let prefix = "x".repeat(10);
            let _res = context.send_message(Bytes::from(prefix)).await;
        }
    }

    async fn disconnected(&mut self, context: ProtocolContextMutRef<'_>) {
        let _res = context.shutdown().await;
    }

    async fn received(&mut self, context: ProtocolContextMutRef<'_>, _data: Bytes) {
        if context.session.ty.is_outbound() {
            let _res = context.shutdown().await;
        }
    }
    async fn poll(&mut self, _context: &mut ProtocolContext) -> Option<()> {
        Some(())
    }
}

fn create_meta(id: ProtocolId) -> ProtocolMeta {
    MetaBuilder::new()
        .id(id)
        .service_handle(move || {
            let handle = Box::new(PHandle);
            ProtocolHandle::Callback(handle)
        })
        .build()
}

pub fn create<F>(meta: ProtocolMeta, shandle: F) -> Service<F, NoopKeyProvider>
where
    F: ServiceHandle + Unpin + 'static,
{
    ServiceBuilder::default()
        .insert_protocol(meta)
        .forever(true)
        .build(shandle)
}

#[test]
fn test_uninterrupter_poll() {
    let mut service_0 = create(create_meta(1.into()), ());
    let mut service_1 = create(create_meta(1.into()), ());
    let rt = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap();

    let (addr_sender, addr_receiver) = channel::oneshot::channel::<Multiaddr>();
    rt.spawn(async move {
        let listen_addr = service_0
            .listen("/ip4/127.0.0.1/tcp/0".parse().unwrap())
            .await
            .unwrap();
        let _res = addr_sender.send(listen_addr);
        service_0.run().await
    });

    rt.block_on(async move {
        let listen_addr = addr_receiver.await.unwrap();
        service_1
            .dial(listen_addr, TargetProtocol::All)
            .await
            .unwrap();
        service_1.run().await
    });
}
