use common::database::{
    player_data::{DbPlayerDataRow, DbUserUidRow, PlayerExtJsonData},
    sdk_data::DbComboTokenRow,
};
use sqlx::{query, query_as, PgPool};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DbError {
    #[error("SQL error: {0}")]
    SqlxError(#[from] sqlx::Error),
    #[error("entry not found")]
    NotFound,
}

pub async fn select_combo_token_by_account(
    pool: &PgPool,
    account_uid: &str,
) -> Result<DbComboTokenRow, DbError> {
    query_as("SELECT * from t_combo_token where account_uid = ($1)")
        .bind(account_uid)
        .fetch_optional(pool)
        .await?
        .ok_or(DbError::NotFound)
}

pub async fn select_player_uid_by_account(
    pool: &PgPool,
    account_uid: &str,
) -> Result<DbUserUidRow, DbError> {
    query_as("SELECT * from t_user_uid where account_uid = ($1)")
        .bind(account_uid)
        .fetch_optional(pool)
        .await?
        .ok_or(DbError::NotFound)
}

pub async fn insert_player_uid_by_account(
    pool: &PgPool,
    account_uid: &str,
    platform: u32,
) -> Result<DbUserUidRow, DbError> {
    let ext = serde_json::to_string(&PlayerExtJsonData {
        reg_platform: platform,
    })
    .unwrap();

    Ok(
        query_as("INSERT INTO t_user_uid (account_uid, ext) VALUES ($1, $2) RETURNING *")
            .bind(account_uid)
            .bind(ext)
            .fetch_one(pool)
            .await?,
    )
}

pub async fn select_player_data_by_uid(
    pool: &PgPool,
    uid: i32,
) -> Result<DbPlayerDataRow, DbError> {
    query_as("SELECT * from t_player_data where uid = ($1)")
        .bind(uid)
        .fetch_optional(pool)
        .await?
        .ok_or(DbError::NotFound)
}

pub async fn insert_player_data_by_uid(
    pool: &PgPool,
    uid: i32,
    bin_data: Vec<u8>,
) -> Result<(), DbError> {
    query("INSERT into t_player_data VALUES ($1, $2)")
        .bind(uid)
        .bind(bin_data)
        .execute(pool)
        .await?;

    Ok(())
}

pub async fn update_player_data_by_uid(
    pool: &PgPool,
    uid: i32,
    bin_data: Vec<u8>,
) -> Result<(), DbError> {
    query("UPDATE t_player_data SET bin_data = ($1) WHERE uid = ($2)")
        .bind(bin_data)
        .bind(uid)
        .execute(pool)
        .await?;

    Ok(())
}
