use derive_builder::Builder;
use redact::Secret;
use sqlx::query_as;
use uuid::Uuid;

use crate::{client::AppDbClient, macros::with_id_query};

/// Information pertaining to a `LOGIN` role used to grant direct PostgreSQL
/// access to a backing database.
#[derive(Clone, Debug)]
pub struct ServiceCred {
    /// Primary key (defaults to UUIDv7).
    pub id: Uuid,

    /// ID of the database cluster on which this role exists.
    pub cluster_id: Uuid,

    /// ID of the user for whom this role was created.
    pub owner_id: Uuid,

    /// Postgres role name.
    pub rolname: String,

    /// Postgres password.
    pub password: Secret<String>,
}

with_id_query!(
    ServiceCred,
    sql = "select * from service_creds where id = $1"
);

impl ServiceCred {
    /// Build an insert statement to save information about a new service
    /// credential role.
    pub fn insert() -> InsertBuilder {
        Default::default()
    }

    /// Query by ID of the user who owns the credential(s).
    pub fn belonging_to_user(owner_id: Uuid) -> BelongingToQuery {
        BelongingToQuery { owner_id }
    }
}

#[derive(Builder, Clone, Debug)]
pub struct Insert {
    cluster_id: Uuid,
    owner_id: Uuid,
    rolname: String,
    password: Secret<String>,
}

impl Insert {
    pub async fn execute(self, app_db: &mut AppDbClient) -> sqlx::Result<ServiceCred> {
        query_as!(
            ServiceCred,
            r#"
insert into service_creds (
    cluster_id,
    owner_id,
    rolname,
    password
) values ($1, $2, $3, $4)
returning *
"#,
            self.cluster_id,
            self.owner_id,
            self.rolname,
            self.password.expose_secret(),
        )
        .fetch_one(app_db.get_conn())
        .await
    }
}

#[derive(Clone, Copy, Debug)]
pub struct BelongingToQuery {
    owner_id: Uuid,
}

impl BelongingToQuery {
    pub async fn fetch_all(&self, app_db: &mut AppDbClient) -> sqlx::Result<Vec<ServiceCred>> {
        query_as!(
            ServiceCred,
            "select * from service_creds where owner_id = $1",
            self.owner_id
        )
        .fetch_all(app_db.get_conn())
        .await
    }
}
