//! RPC modes and shared wire values

use std::fmt;
use std::str::FromStr;
use std::time::Duration;

use efdn_types::{Timestamp, TypeError};
use serde::de;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

pub mod error;
pub mod id;
pub mod mode;
pub mod number;
pub mod text;

use error::WireError;
use number::{ByteCount, FrameCount, Seq};
use text::{MetadataKey, MetadataValue};

#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Hash,
    PartialOrd,
    Ord,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
pub struct WireVersion(u16);

impl WireVersion {
    pub const V1: Self = Self(1);

    pub fn new(value: u16) -> Result<Self, TypeError> {
        if value == 0 {
            return Err(TypeError::new(
                "WireVersion",
                "version must be greater than zero",
                "provide a version like 1",
            ));
        }
        Ok(Self(value))
    }

    pub const fn get(self) -> u16 {
        self.0
    }
}

impl fmt::Display for WireVersion {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "v{}", self.0)
    }
}

impl FromStr for WireVersion {
    type Err = TypeError;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        let value = value.strip_prefix('v').unwrap_or(value);
        let value = value.parse::<u16>().map_err(|error| {
            TypeError::new(
                "WireVersion",
                format!("expected version like v1 or 1: {error}"),
                "provide a version like v1",
            )
        })?;
        Self::new(value)
    }
}

impl Serialize for WireVersion {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.to_string())
    }
}

impl<'de> Deserialize<'de> for WireVersion {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = String::deserialize(deserializer)?;
        value.parse().map_err(de::Error::custom)
    }
}

#[derive(
    Clone,
    Debug,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
#[serde(tag = "status", content = "data", rename_all = "snake_case")]
pub enum Reply<T> {
    Ok(T),
    Err(WireError),
}

impl<T> Reply<T> {
    pub fn error(error: impl Into<WireError>) -> Self {
        Self::Err(error.into())
    }

    pub fn into_result(self) -> Result<T, WireError> {
        match self {
            Self::Ok(value) => Ok(value),
            Self::Err(error) => Err(error),
        }
    }
}

impl<T, E> From<Result<T, E>> for Reply<T>
where
    E: Into<WireError>,
{
    fn from(result: Result<T, E>) -> Self {
        match result {
            Ok(value) => Self::Ok(value),
            Err(error) => Self::Err(error.into()),
        }
    }
}

#[derive(
    Clone,
    Debug,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum In<T> {
    Item { seq: Seq, body: T },
    End { total: FrameCount },
}

#[derive(
    Clone,
    Debug,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum Out<T> {
    Item { seq: Seq, body: T },
    End(End),
    Err(WireError),
}

impl<T> Out<T> {
    pub fn error(error: impl Into<WireError>) -> Self {
        Self::Err(error.into())
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum Flow {
    Credit { bytes: ByteCount },
    Cancel,
}

#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
pub struct End {
    pub total: FrameCount,
    pub truncated: bool,
}

#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Default,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
pub struct Ack;

#[derive(
    Clone,
    Debug,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
pub struct Deadline(Timestamp);

impl Deadline {
    pub fn new(value: Timestamp) -> Self {
        Self(value)
    }

    pub const fn timestamp(&self) -> &Timestamp {
        &self.0
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct Timeout(Duration);

impl Timeout {
    pub fn new(value: Duration) -> Result<Self, TypeError> {
        if value.is_zero() {
            return Err(TypeError::new(
                "Timeout",
                "timeout must be greater than zero",
                "provide a timeout like 2s",
            ));
        }
        Ok(Self(value))
    }

    pub const fn duration(self) -> Duration {
        self.0
    }
}

#[derive(
    Clone,
    Debug,
    Default,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    rkyv::Archive,
    rkyv::Serialize,
    rkyv::Deserialize,
)]
pub struct WireMetadata(Vec<(MetadataKey, MetadataValue)>);

impl WireMetadata {
    pub fn new() -> Self {
        Self(Vec::new())
    }

    pub fn insert(&mut self, key: MetadataKey, value: MetadataValue) {
        self.0.push((key, value));
    }

    pub fn iter(&self) -> impl Iterator<Item = (&MetadataKey, &MetadataValue)> {
        self.0.iter().map(|(key, value)| (key, value))
    }
}
