use std::fmt::{Display, Error as FmtError, Formatter};
use futures::channel::{mpsc, oneshot};
use crate::{
    io::{ReceiveError, SendError},
    metrics::Metrics,
    Data, PublicKey, SecretKey, Splittable,
};
mod handshake;
mod negotiation;
mod v1;
use handshake::HandshakeError;
pub use negotiation::{protocol, ProtocolNegotiationError};
pub type Version = u32;
pub type ResultForService<PK, D> = (PK, Option<mpsc::UnboundedSender<D>>);
#[derive(Debug, PartialEq, Eq)]
pub enum Protocol {
    V1,
}
#[derive(Debug)]
pub enum ProtocolError<PK: PublicKey> {
    HandshakeError(HandshakeError<PK>),
    SendError(SendError),
    ReceiveError(ReceiveError),
    CardiacArrest,
    NoParentConnection,
    NoUserConnection,
    NotAuthorized,
    SendTimeout,
}
impl<PK: PublicKey> Display for ProtocolError<PK> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
        use ProtocolError::*;
        match self {
            HandshakeError(e) => write!(f, "handshake error: {e}"),
            SendError(e) => write!(f, "send error: {e}"),
            ReceiveError(e) => write!(f, "receive error: {e}"),
            CardiacArrest => write!(f, "heartbeat stopped"),
            NoParentConnection => write!(f, "cannot send result to service"),
            NoUserConnection => write!(f, "cannot send data to user"),
            NotAuthorized => write!(f, "peer not authorized"),
            SendTimeout => write!(f, "send timed out"),
        }
    }
}
impl<PK: PublicKey> From<HandshakeError<PK>> for ProtocolError<PK> {
    fn from(e: HandshakeError<PK>) -> Self {
        ProtocolError::HandshakeError(e)
    }
}
impl<PK: PublicKey> From<SendError> for ProtocolError<PK> {
    fn from(e: SendError) -> Self {
        ProtocolError::SendError(e)
    }
}
impl<PK: PublicKey> From<ReceiveError> for ProtocolError<PK> {
    fn from(e: ReceiveError) -> Self {
        ProtocolError::ReceiveError(e)
    }
}
impl Protocol {
    const MIN_VERSION: Version = 1;
    const MAX_VERSION: Version = 1;
    pub async fn manage_incoming<SK: SecretKey, D: Data, S: Splittable>(
        &self,
        stream: S,
        secret_key: SK,
        result_for_parent: mpsc::UnboundedSender<ResultForService<SK::PublicKey, D>>,
        data_for_user: mpsc::UnboundedSender<D>,
        authorization_requests_sender: mpsc::UnboundedSender<(
            SK::PublicKey,
            oneshot::Sender<bool>,
        )>,
        metrics: Metrics,
    ) -> Result<(), ProtocolError<SK::PublicKey>> {
        use Protocol::*;
        match self {
            V1 => {
                v1::incoming(
                    stream,
                    secret_key,
                    authorization_requests_sender,
                    result_for_parent,
                    data_for_user,
                    metrics,
                )
                .await
            }
        }
    }
    pub async fn manage_outgoing<SK: SecretKey, D: Data, S: Splittable>(
        &self,
        stream: S,
        secret_key: SK,
        public_key: SK::PublicKey,
        result_for_service: mpsc::UnboundedSender<ResultForService<SK::PublicKey, D>>,
        data_for_user: mpsc::UnboundedSender<D>,
        metrics: Metrics,
    ) -> Result<(), ProtocolError<SK::PublicKey>> {
        use Protocol::*;
        match self {
            V1 => {
                v1::outgoing(
                    stream,
                    secret_key,
                    public_key,
                    result_for_service,
                    data_for_user,
                    metrics,
                )
                .await
            }
        }
    }
}
impl TryFrom<Version> for Protocol {
    type Error = Version;
    fn try_from(version: Version) -> Result<Self, Self::Error> {
        match version {
            1 => Ok(Protocol::V1),
            unknown_version => Err(unknown_version),
        }
    }
}