use anyhow::Result;
use diesel::*;
use serde::{de::DeserializeOwned, Serialize};

#[derive(Insertable, Queryable, AsChangeset)]
#[diesel(table_name = crate::schema::settings)]
struct SettingsRow<'a> {
    key: &'a str,
    value: &'a [u8],
}

pub struct Settings<'a> {
    connection: &'a mut SqliteConnection,
}

impl<'a> Settings<'a> {
    pub fn new(connection: &'a mut SqliteConnection) -> Self {
        Self { connection }
    }

    pub fn put<T: Serialize>(&mut self, k: &str, v: &T) -> Result<()> {
        use crate::schema::settings::dsl::*;
        let bytes = bincode::serialize(v)?;

        diesel::insert_into(settings)
            .values(SettingsRow {
                key: k,
                value: &bytes,
            })
            .on_conflict(key)
            .do_update()
            .set(value.eq(&bytes))
            .execute(self.connection)?;

        Ok(())
    }

    pub fn get<T: DeserializeOwned>(&mut self, k: &str) -> Result<Option<T>> {
        use crate::schema::settings::dsl::*;
        let blob: Option<Vec<u8>> = settings
            .select(value)
            .filter(key.eq(k))
            .first(self.connection)
            .optional()?;

        Ok(match blob {
            Some(b) => Some(bincode::deserialize(&b)?),
            None => None,
        })
    }

    pub fn del(&mut self, k: &str) -> Result<usize> {
        use crate::schema::settings::dsl::*;
        Ok(diesel::delete(settings.filter(key.eq(k))).execute(self.connection)?)
    }

    pub fn list_keys(&mut self) -> Result<Vec<String>> {
        use crate::schema::settings::dsl::*;
        let keys: Vec<String> = settings.select(key).load(self.connection)?;

        Ok(keys)
    }
}
