use std::fmt::{Debug, Display};

use rusqlite::{
    ToSql,
    types::{FromSql, ToSqlOutput},
};
use serde::Deserialize;

use crate::types::user::FromRow;

#[derive(Clone, PartialEq, Eq, Hash, Deserialize)]
pub struct Password(String);

impl Password {
    pub fn password(&self) -> &str {
        &self.0
    }
}

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

impl Debug for Password {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Password: <redacted>")
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PasswordHash(pub String);

impl FromRow for PasswordHash {
    fn try_from_row(row: &rusqlite::Row) -> Result<Self, rusqlite::Error> {
        Ok(Self(row.get("password_hash")?))
    }
}

impl ToSql for PasswordHash {
    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
        Ok(ToSqlOutput::Borrowed(rusqlite::types::ValueRef::Text(
            self.0.as_bytes(),
        )))
    }
}

impl FromSql for PasswordHash {
    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
        match value {
            rusqlite::types::ValueRef::Text(items) => {
                let hash = unsafe { String::from_utf8_unchecked(items.to_vec()) };
                let hash = PasswordHash(hash);

                Ok(hash)
            }
            _ => Err(rusqlite::types::FromSqlError::InvalidType),
        }
    }
}
