use diesel::backend::Backend;
use diesel::deserialize::{self, FromSql};
use diesel::serialize::{self, IsNull, Output, ToSql};
use diesel::sql_types::Text;
use diesel::sqlite::Sqlite;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::{fmt, str};

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[diesel(sql_type = Text)]
pub struct ArticleID(Arc<String>);

impl ArticleID {
    pub fn new(id: &str) -> Self {
        Self::from_owned(id.into())
    }

    pub fn from_owned(id: String) -> Self {
        ArticleID(Arc::new(id))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

impl FromSql<Text, Sqlite> for ArticleID {
    fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
        let bytes = <Vec<u8>>::from_sql(bytes)?;
        let string = str::from_utf8(&bytes)?;
        Ok(ArticleID::new(string))
    }
}

impl ToSql<Text, Sqlite> for ArticleID {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
        out.set_value(self.as_str());
        Ok(IsNull::No)
    }
}

//------------------------------------------------------------------

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[diesel(sql_type = Text)]
pub struct FeedID(Arc<String>);
impl FeedID {
    pub fn new(id: &str) -> Self {
        Self::from_owned(id.into())
    }

    pub fn from_owned(id: String) -> Self {
        FeedID(Arc::new(id))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

impl FromSql<Text, Sqlite> for FeedID {
    fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
        let bytes = <Vec<u8>>::from_sql(bytes)?;
        let string = str::from_utf8(&bytes)?;
        Ok(FeedID::new(string))
    }
}

impl ToSql<Text, Sqlite> for FeedID {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
        out.set_value(self.as_str());
        Ok(IsNull::No)
    }
}

//------------------------------------------------------------------

pub static NEWSFLASH_TOPLEVEL: Lazy<CategoryID> = Lazy::new(|| CategoryID::new("NewsFlash.Toplevel"));

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[diesel(sql_type = Text)]
pub struct CategoryID(Arc<String>);
impl CategoryID {
    pub fn new(id: &str) -> Self {
        Self::from_owned(id.into())
    }

    pub fn from_owned(id: String) -> Self {
        CategoryID(Arc::new(id))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

impl FromSql<Text, Sqlite> for CategoryID {
    fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
        let bytes = <Vec<u8>>::from_sql(bytes)?;
        let string = str::from_utf8(&bytes)?;
        Ok(CategoryID::new(string))
    }
}

impl ToSql<Text, Sqlite> for CategoryID {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
        out.set_value(self.as_str());
        Ok(IsNull::No)
    }
}

//------------------------------------------------------------------

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[diesel(sql_type = Text)]
pub struct TagID(Arc<String>);
impl TagID {
    pub fn new(id: &str) -> Self {
        Self::from_owned(id.into())
    }

    pub fn from_owned(id: String) -> Self {
        TagID(Arc::new(id))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

impl FromSql<Text, Sqlite> for TagID {
    fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
        let bytes = <Vec<u8>>::from_sql(bytes)?;
        let string = str::from_utf8(&bytes)?;
        Ok(TagID::new(string))
    }
}

impl ToSql<Text, Sqlite> for TagID {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
        out.set_value(self.as_str());
        Ok(IsNull::No)
    }
}

//------------------------------------------------------------------

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[diesel(sql_type = Text)]
pub struct PluginID(Arc<String>);
impl PluginID {
    pub fn new(id: &str) -> Self {
        Self::from_owned(id.into())
    }

    pub fn from_owned(id: String) -> Self {
        PluginID(Arc::new(id))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

impl FromSql<Text, Sqlite> for PluginID {
    fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
        let bytes = <Vec<u8>>::from_sql(bytes)?;
        let string = str::from_utf8(&bytes)?;
        Ok(PluginID::new(string))
    }
}

impl ToSql<Text, Sqlite> for PluginID {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
        out.set_value(self.as_str());
        Ok(IsNull::No)
    }
}
