use crate::connections::db_connection::db;
use crate::controllers::server_controller::ServerConstraints;
use crate::models::accounts::{Account, AccountID, AccountType, NewAccount};
use crate::models::users::UserID;
use crate::schema::accounts;
use crate::utils::time_utils::time;
use diesel::RunQueryDsl;
use diesel::prelude::*;

#[derive(serde::Deserialize)]
pub struct UpdateAccountQuery {
    pub name: String,
    pub r#type: AccountType,
}

impl UpdateAccountQuery {
    pub fn check_error(&self) -> Option<&'static str> {
        let constraints = ServerConstraints::default();

        if !constraints.account_name.check_str(&self.name) {
            return Some("Invalid account name length!");
        }

        None
    }
}

/// Create a new account
pub async fn create(user_id: UserID, query: &UpdateAccountQuery) -> anyhow::Result<Account> {
    let new_account = NewAccount {
        name: query.name.as_str(),
        user_id: user_id.0,
        time_create: time() as i64,
        time_update: time() as i64,
        type_: query.r#type.to_string(),
    };

    let res: Account = diesel::insert_into(accounts::table)
        .values(&new_account)
        .get_result(&mut db()?)?;

    update(res.id(), query).await?;

    Ok(res)
}

/// Update an account
pub async fn update(id: AccountID, q: &UpdateAccountQuery) -> anyhow::Result<()> {
    diesel::update(accounts::dsl::accounts.filter(accounts::dsl::id.eq(id.0)))
        .set((
            accounts::dsl::time_update.eq(time() as i64),
            accounts::dsl::name.eq(&q.name),
            accounts::dsl::type_.eq(&q.r#type.to_string()),
        ))
        .execute(&mut db()?)?;

    Ok(())
}

/// Get a single account by its id
pub async fn get_by_id(account_id: AccountID) -> anyhow::Result<Account> {
    Ok(accounts::table
        .filter(accounts::dsl::id.eq(account_id.0))
        .get_result(&mut db()?)?)
}

/// Get the accounts of a user
pub async fn get_list_user(id: UserID) -> anyhow::Result<Vec<Account>> {
    Ok(accounts::table
        .filter(accounts::dsl::user_id.eq(id.0))
        .get_results(&mut db()?)?)
}

/// Change the default account of a user
pub async fn set_default(user_id: UserID, account_id: AccountID) -> anyhow::Result<()> {
    diesel::update(accounts::dsl::accounts.filter(accounts::dsl::user_id.eq(user_id.0)))
        .set((accounts::dsl::default_account.eq(false),))
        .execute(&mut db()?)?;

    diesel::update(
        accounts::dsl::accounts.filter(
            accounts::dsl::user_id
                .eq(user_id.0)
                .and(accounts::dsl::id.eq(account_id.0)),
        ),
    )
    .set((accounts::dsl::default_account.eq(true),))
    .execute(&mut db()?)?;

    Ok(())
}

/// Delete an account
pub async fn delete(id: AccountID) -> anyhow::Result<()> {
    diesel::delete(accounts::dsl::accounts.filter(accounts::dsl::id.eq(id.0)))
        .execute(&mut db()?)?;

    Ok(())
}

/// Delete all the accounts of a user
pub async fn delete_all_user(user: UserID) -> anyhow::Result<()> {
    for account in get_list_user(user).await? {
        delete(account.id()).await?;
    }
    Ok(())
}
