use crate::jwks::Jwks;
use serde::{Deserialize, Serialize};
use sqlx::{Error, FromRow, Sqlite, SqliteConnection, query, query_as, sqlite::SqliteQueryResult};
use time::{Duration, UtcDateTime};
use uuid::Uuid;

///
/// The response from a ring server upon a successful
/// invite redemption. Allows population of a [JoinedRing] record.
///
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct InviteResponse {
    pub url: String,
    pub name: String,
    pub description: String,
    pub manifest_version: i64,
    pub keyset: Jwks,
}

///
/// The invitation for a new [Member] from the
/// perspective of the [Ring] server.
///
#[derive(FromRow, Debug, Serialize, Clone)]
pub struct Invite {
    pub id: i64,
    pub url: String,
    pub invite_code: String,
    pub expiration: i64,
    pub ring_id: i64,
    pub redeemed: bool,
}

impl Invite {
    /// Creates a new Invite with a unique invite code
    pub fn new(url: String, ring_id: i64, expiration_hours: i64) -> Self {
        let now = UtcDateTime::now();
        let expiration_time = now + Duration::hours(expiration_hours);
        let expiration = expiration_time.unix_timestamp();
        let invite_code = Uuid::new_v4().hyphenated().to_string();
        Invite {
            id: 0,
            url,
            invite_code,
            expiration,
            ring_id,
            redeemed: false,
        }
    }

    ///
    /// Creates a new invite for redemption by new ring members.
    /// Invites are shared outside the Ringspace application, via Signal, email, or
    /// other means.
    ///
    pub async fn save(&self, conn: &mut SqliteConnection) -> Result<SqliteQueryResult, Error> {
        if self.id == 0 {
            return query(
                "
                INSERT INTO invites (url, invite_code, ring_id, expiration)
                VALUES (?, ?, ?, ?)
                ",
            )
            .bind(&self.url)
            .bind(&self.invite_code)
            .bind(self.ring_id)
            .bind(self.expiration)
            .execute(&mut *conn)
            .await;
        }

        query(
            "
            UPDATE invites SET
                url = ?,
                invite_code = ?,
                ring_id = ?,
                expiration = ?,
                redeemed = ?
            WHERE id = ?
            ",
        )
        .bind(&self.url)
        .bind(&self.invite_code)
        .bind(self.ring_id)
        .bind(self.expiration)
        .bind(self.redeemed)
        .bind(self.id)
        .execute(&mut *conn)
        .await
    }

    ///
    /// Deletes a single invite by ID
    ///
    pub async fn delete(self, conn: &mut SqliteConnection) -> Result<SqliteQueryResult, Error> {
        query(
            "
            DELETE FROM invites
            WHERE id = ?
            ",
        )
        .bind(self.id)
        .execute(&mut *conn)
        .await
    }
}

///
/// Lists all invites
///
pub async fn list(conn: &mut SqliteConnection) -> Result<Vec<Invite>, Error> {
    query_as::<Sqlite, Invite>(
        "
        SELECT * FROM invites;
        ",
    )
    .fetch_all(&mut *conn)
    .await
}

///
/// Retrieves a single [Invite] by ID.
///
pub async fn get_by_id(conn: &mut SqliteConnection, invite_id: i64) -> Result<Invite, Error> {
    query_as::<sqlx::Sqlite, Invite>(
        "
        SELECT * FROM invites;
        WHERE id = ?
        ",
    )
    .bind(invite_id)
    .fetch_one(&mut *conn)
    .await
}

///
/// Retrieves a single [Invite] by invite code.
///
pub async fn get_by_code(conn: &mut SqliteConnection, invite_code: &str) -> Result<Invite, Error> {
    query_as::<sqlx::Sqlite, Invite>(
        "
        SELECT * FROM invites
        WHERE invite_code = ?
        ",
    )
    .bind(invite_code)
    .fetch_one(&mut *conn)
    .await
}
