use chrono::{DateTime, Utc};
use derive_builder::Builder;
use interim_pgtypes::pg_acl::PgPrivilegeType;
use sqlx::{postgres::types::Oid, query_as};
use uuid::Uuid;

use crate::client::AppDbClient;

#[derive(Clone, Debug)]
pub struct RelInvitation {
    pub id: Uuid,
    pub email: String,
    pub workspace_id: Uuid,
    pub class_oid: Oid,
    pub created_by: Uuid,
    pub privilege: String,
    pub expires_at: Option<DateTime<Utc>>,
}

impl RelInvitation {
    pub fn belonging_to_rel(rel_oid: Oid) -> BelongingToRelQuery {
        BelongingToRelQuery { rel_oid }
    }

    pub fn upsertable() -> UpsertableRelInvitationBuilder {
        UpsertableRelInvitationBuilder::default()
    }
}

#[derive(Clone, Debug)]
pub struct BelongingToRelQuery {
    rel_oid: Oid,
}

impl BelongingToRelQuery {
    pub async fn fetch_all(
        self,
        app_db: &mut AppDbClient,
    ) -> Result<Vec<RelInvitation>, sqlx::Error> {
        query_as!(
            RelInvitation,
            "
select * from rel_invitations
where class_oid = $1
",
            self.rel_oid
        )
        .fetch_all(&mut *app_db.conn)
        .await
    }
}

#[derive(Builder, Clone, Debug)]
pub struct UpsertableRelInvitation {
    email: String,
    workspace_id: Uuid,
    class_oid: Oid,
    created_by: Uuid,
    privilege: PgPrivilegeType,
    #[builder(default, setter(strip_option))]
    expires_at: Option<DateTime<Utc>>,
}

impl UpsertableRelInvitation {
    pub async fn upsert(self, app_db: &mut AppDbClient) -> Result<RelInvitation, sqlx::Error> {
        query_as!(
            RelInvitation,
            "
insert into rel_invitations
(email, workspace_id, class_oid, privilege, created_by, expires_at)
values ($1, $2, $3, $4, $5, $6)
on conflict (email, workspace_id, class_oid, privilege) do update set
    created_by = excluded.created_by,
    expires_at = excluded.expires_at
returning *
",
            self.email,
            self.workspace_id,
            self.class_oid,
            self.privilege.to_abbrev().to_string(),
            self.created_by,
            self.expires_at,
        )
        .fetch_one(&mut *app_db.conn)
        .await
    }
}
