use std::marker::PhantomData;

use ftypes::{Error, Store};
use futures::SinkExt;
use url::Url;

use crate::{ManagerHandle, SetupDecision, SignalClient};

macro_rules! send_and_receive {
    ($s:expr, SignalClientChannelValue::$name:tt ($($arg:expr),*)) => {{
        let (sender, receiver) = futures::channel::oneshot::channel();
        log::trace!("SignalClientChannel sending request for {}", stringify!($name));
        $s.send(SignalClientChannelValue::$name($($arg,)* sender))
            .await;
        log::trace!("SignalClientChannel awaiting response for {}", stringify!($name));
        receiver
            .await
            .expect("Failed to receive results from the SignalClientChannelReceiver")
    }}
}

enum SignalClientChannelValue<S: Store> {
    RequestSetupDecision(futures::channel::oneshot::Sender<SetupDecision>),
    RequestQrcodeScan(Url, futures::channel::oneshot::Sender<()>),
    RequestConfirmationCode(futures::channel::oneshot::Sender<String>),
    SetupComplete(ManagerHandle<S>, futures::channel::oneshot::Sender<()>),
    OnMessage(
        ftypes::ReceivedMessage,
        futures::channel::oneshot::Sender<()>,
    ),
    QueueEmpty(futures::channel::oneshot::Sender<()>),
    Error(ftypes::Error, futures::channel::oneshot::Sender<()>),
}

#[derive(Clone)]
pub struct SignalClientChannelSender<C: SignalClient<S>, S: Store> {
    sender: futures::channel::mpsc::Sender<SignalClientChannelValue<S>>,
    // Cannot use `C` directly, as then the channel sender would inherit the Send/Sync properties of C.
    _phantom: PhantomData<fn() -> C>,
}

pub struct SignalClientChannelReceiver<C: SignalClient<S>, S: Store> {
    receiver: futures::channel::mpsc::Receiver<SignalClientChannelValue<S>>,
    inner: C,
}

pub fn channel<C: SignalClient<S>, S: Store>(
    client: C,
) -> (
    SignalClientChannelSender<C, S>,
    SignalClientChannelReceiver<C, S>,
) {
    let (sender, receiver) = futures::channel::mpsc::channel(1);
    (
        SignalClientChannelSender {
            sender,
            _phantom: Default::default(),
        },
        SignalClientChannelReceiver {
            receiver,
            inner: client,
        },
    )
}

impl<C: SignalClient<S>, S: Store> SignalClientChannelSender<C, S> {
    async fn send(&self, val: SignalClientChannelValue<S>) {
        self.sender
            .clone()
            .send(val)
            .await
            .expect("SignalClientChannelSender was used after SignalClientChannelReceiver exited");
    }
}

impl<C: SignalClient<S>, S: Store> SignalClient<S> for SignalClientChannelSender<C, S> {
    async fn request_setup_decision(&self) -> SetupDecision {
        send_and_receive!(self, SignalClientChannelValue::RequestSetupDecision())
    }

    async fn request_qrcode_scan(&self, url: Url) {
        send_and_receive!(self, SignalClientChannelValue::RequestQrcodeScan(url))
    }

    async fn request_confirmation_code(&self) -> String {
        send_and_receive!(self, SignalClientChannelValue::RequestConfirmationCode())
    }

    async fn setup_complete(&self, handle: ManagerHandle<S>) {
        send_and_receive!(self, SignalClientChannelValue::SetupComplete(handle))
    }

    async fn on_message(&self, message: ftypes::ReceivedMessage) {
        send_and_receive!(self, SignalClientChannelValue::OnMessage(message))
    }

    async fn queue_empty(&self) {
        send_and_receive!(self, SignalClientChannelValue::QueueEmpty())
    }

    async fn error(&self, error: Error) {
        send_and_receive!(self, SignalClientChannelValue::Error(error))
    }
}

impl<C: SignalClient<S>, S: Store> SignalClientChannelReceiver<C, S> {
    pub async fn receive(mut self) {
        while let Ok(value) = self.receiver.recv().await {
            match value {
                SignalClientChannelValue::RequestSetupDecision(sender) => sender
                    .send(self.inner.request_setup_decision().await)
                    .expect("SignalClientChannelReceiver could not send back result to Sender"),
                SignalClientChannelValue::RequestQrcodeScan(url, sender) => sender
                    .send(self.inner.request_qrcode_scan(url).await)
                    .expect("SignalClientChannelReceiver could not send back result to Sender"),
                SignalClientChannelValue::RequestConfirmationCode(sender) => sender
                    .send(self.inner.request_confirmation_code().await)
                    .expect("SignalClientChannelReceiver could not send back result to Sender"),
                SignalClientChannelValue::SetupComplete(manager_handle, sender) => sender
                    .send(self.inner.setup_complete(manager_handle).await)
                    .expect("SignalClientChannelReceiver could not send back result to Sender"),
                SignalClientChannelValue::OnMessage(message, sender) => sender
                    .send(self.inner.on_message(message).await)
                    .expect("SignalClientChannelReceiver could not send back result to Sender"),
                SignalClientChannelValue::QueueEmpty(sender) => sender
                    .send(self.inner.queue_empty().await)
                    .expect("SignalClientChannelReceiver could not send back result to Sender"),
                SignalClientChannelValue::Error(error, sender) => sender
                    .send(self.inner.error(error).await)
                    .expect("SignalClientChannelReceiver could not send back result to Sender"),
            }
        }
    }
}
