use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;

#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
pub struct JobState {
    pub name: String,
}

impl JobState {
    pub fn new(name: String) -> Self {
        Self { name }
    }
}

lazy_static! {
    pub static ref JOB_STATE_CREATE: JobState = JobState::new("job.state.create".into());
    pub static ref JOB_STATE_FINISH: JobState = JobState::new("job.state.finish".into());
    pub static ref JOB_STATE_RUNNING: JobState =
        JobState::new("job.state.running".into());
    pub static ref JOB_STATES: [&'static JobState; 3] =
        [&*JOB_STATE_CREATE, &*JOB_STATE_FINISH, &*JOB_STATE_RUNNING];
}

async fn job_state_exists(
    db: &PgPool,
    job_state: &JobState,
) -> sqlx::error::Result<bool> {
    let res = sqlx::query!(
         "SELECT EXISTS (SELECT 1 from survey_mcaptcha_upload_job_states WHERE name = $1)",
         job_state.name,
     )
     .fetch_one(db)
     .await?;

    let mut resp = false;
    if let Some(x) = res.exists {
        resp = x;
    }

    Ok(resp)
}

async fn create_job_states(db: &PgPool) -> sqlx::error::Result<()> {
    for j in &*JOB_STATES {
        if !job_state_exists(db, j).await? {
            sqlx::query!(
                "INSERT INTO survey_mcaptcha_upload_job_states 
              (name) VALUES ($1) ON CONFLICT (name) DO NOTHING;",
                j.name
            )
            .execute(db)
            .await?;
        }
    }
    Ok(())
}

pub async fn migrate_db(db: &PgPool) -> sqlx::error::Result<()> {
    sqlx::migrate!("./migrations/").run(db).await?;
    create_job_states(db).await?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[actix_rt::test]
    async fn test_mcaptcha_job_states_exist() {
        // can't use crate::tests::get_test_data because this module is used by
        // ./src/tests-migrate.rs too, which doesn't load tests module
        let settings = crate::settings::Settings::new().unwrap();
        let db = sqlx::postgres::PgPoolOptions::new()
            .max_connections(2)
            .connect(&settings.database.url)
            .await
            .expect("Unable to form database pool");

        migrate_db(&db).await.unwrap();

        for e in (*JOB_STATES).iter() {
            println!("checking job state {}", e.name);
            assert!(job_state_exists(&db, e).await.unwrap());
        }
    }
}
