use std::sync::Arc;

use derive_builder::UninitializedFieldError;
use uuid::Uuid;

pub mod portal;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Actor {
    /// Bypass explicit auth checks.
    Bypass,

    User(Uuid),
}

/// Encompasses all possible (non-fatal) errors encountered while executing an
/// [`Accessor`]'s `fetch_one()` or `fetch_optional()` methods.
#[derive(Clone, Debug, thiserror::Error)]
pub enum AccessError {
    #[error("database error: {0}")]
    Database(Arc<sqlx::Error>),

    #[error("not found or access denied")]
    NotFound,

    #[error("incomplete access spec: {0}")]
    Incomplete(UninitializedFieldError),
}

impl From<sqlx::Error> for AccessError {
    fn from(value: sqlx::Error) -> Self {
        Self::Database(Arc::new(value))
    }
}

impl From<UninitializedFieldError> for AccessError {
    fn from(value: UninitializedFieldError) -> Self {
        Self::Incomplete(value)
    }
}

/// Provides methods for fetching database entities of type `T`, typically with
/// authorization checks.
pub trait Accessor<T>: Default + Sized {
    /// Returns a new builder struct. Alias for [`Self::default()`].
    fn new() -> Self {
        Self::default()
    }

    /// Fetch an entity from the database, returning [`AccessError::NotFound`]
    /// if access is denied or the object cannot be found.
    fn fetch_one(self) -> impl Future<Output = Result<T, AccessError>>;
}
