use std::{borrow::Borrow, fmt, sync::Arc};

use bstr::{BStr, ByteSlice};
use serde::{Deserialize, Serialize, de::Visitor};

#[derive(Clone, Eq, PartialEq, Hash)]
pub struct ContigName(Arc<[u8]>);

impl ContigName {
    pub fn new(name: &[u8]) -> Self {
        Self(Arc::from(name))
    }

    pub fn as_str(&self) -> Result<&str, bstr::Utf8Error> {
        self.0.to_str()
    }
}

impl From<&[u8]> for ContigName {
    fn from(s: &[u8]) -> Self {
        Self(Arc::from(s))
    }
}

impl From<&str> for ContigName {
    fn from(s: &str) -> Self {
        Self(Arc::from(s.as_bytes()))
    }
}

impl From<String> for ContigName {
    fn from(s: String) -> Self {
        Self(Arc::from(s.as_bytes()))
    }
}

impl From<Arc<[u8]>> for ContigName {
    fn from(s: Arc<[u8]>) -> Self {
        Self(s)
    }
}

impl AsRef<BStr> for ContigName {
    fn as_ref(&self) -> &BStr {
        self.0.as_bstr()
    }
}

impl Borrow<[u8]> for ContigName {
    fn borrow(&self) -> &[u8] {
        &self.0
    }
}

impl fmt::Display for ContigName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.0.to_str_lossy())
    }
}

impl fmt::Debug for ContigName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("ContigName")
            .field(&self.0.to_str_lossy())
            .finish()
    }
}

impl PartialEq<[u8]> for ContigName {
    fn eq(&self, other: &[u8]) -> bool {
        self.as_ref() == other
    }
}

impl Serialize for ContigName {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_bytes(&self.0)
    }
}

impl<'de> Deserialize<'de> for ContigName {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct ByteVisitor;

        impl Visitor<'_> for ByteVisitor {
            type Value = ContigName;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a byte array")
            }

            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Ok(ContigName(Arc::from(v)))
            }
        }

        deserializer.deserialize_bytes(ByteVisitor)
    }
}
