use diesel::{
    backend::Backend,
    deserialize::{self, FromSql},
    serialize::{self, IsNull, Output, ToSql},
    sql_types::Integer,
    sqlite::Sqlite,
};
use serde::{Deserialize, Serialize};

use crate::schema::offline_actions;

use super::{ArticleID, TagID};

#[derive(Eq, PartialEq, Copy, Clone, Debug, AsExpression, FromSqlRow, Serialize, Deserialize, Hash)]
#[diesel(sql_type = Integer)]
pub enum OfflineActionType {
    Read,
    Unread,
    Mark,
    Unmark,
    Tag,
    Untag,
}

impl From<i32> for OfflineActionType {
    fn from(value: i32) -> Self {
        match value {
            0 => Self::Read,
            1 => Self::Unread,
            2 => Self::Mark,
            3 => Self::Unmark,
            4 => Self::Tag,
            5 => Self::Untag,

            _ => Self::Read,
        }
    }
}

impl FromSql<Integer, Sqlite> for OfflineActionType {
    fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
        let int = i32::from_sql(bytes)?;
        Ok(OfflineActionType::from(int))
    }
}

impl ToSql<Integer, Sqlite> for OfflineActionType {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
        out.set_value(*self as i32);
        Ok(IsNull::No)
    }
}

#[derive(Identifiable, Insertable, Queryable, Clone, Eq, PartialEq, Debug)]
#[diesel(primary_key(action_type, article_id, tag_id))]
#[diesel(table_name = offline_actions)]
#[diesel(check_for_backend(SQLite))]
pub struct OfflineAction {
    pub action_type: OfflineActionType,
    pub article_id: ArticleID,
    pub tag_id: Option<TagID>,
}
