use sqlx::{query, query_as, Connection, SqliteConnection};

use crate::secrets::UserToken;

// TODO: allow configuring this via envar
const DB_URI_DEFAULT: &str = "sqlite://sqlite.db";

pub trait Database {
    async fn get_token(&mut self, username: &str) -> Result<UserToken, sqlx::Error>;
}

#[derive(Debug)]
pub struct SqliteDatabase {
    conn: SqliteConnection,
}

impl SqliteDatabase {
    pub async fn open() -> Self {
        let mut db = Self {
            conn: SqliteConnection::connect(DB_URI_DEFAULT)
                .await
                .expect("Failed to open SQLite database"),
        };

        db.init().await;

        db
    }

    pub async fn init(&mut self) {
        query(
            "CREATE TABLE IF NOT EXISTS user_tokens (
                id          INTEGER PRIMARY KEY NOT NULL,
                username    VARCHAR(255),
                token       NCHAR(30),
                created     DATETIME,
                valid       BOOLEAN,
            );",
        )
        .execute(&mut self.conn)
        .await
        .expect("Failed to initialize table user_tokens");
    }
}

impl Database for SqliteDatabase {
    async fn get_token(&mut self, username: &str) -> Result<UserToken, sqlx::Error> {
        let row: (String,) = query_as(
            "SELECT token
            FROM user_tokens
            WHERE
                username = {username}
                AND valid = TRUE
            ORDER BY
                created DESC",
        )
        .bind(username)
        .fetch_one(&mut self.conn)
        .await?;

        Ok(UserToken::from(row.0))
    }
}