use std::error::Error;

use mysql_async::{Pool, prelude::*};

pub async fn load_data(pool: &Pool) -> Result<(), Box<dyn Error>> {
    // Obviously use something better/more robust here if you're a) loading
    // other data sources and b) aren't so lucky as to have a single `;` after
    // each block of SQL. :)
    let data = String::from_utf8_lossy(include_bytes!("../data.sql"));
    for stmt in data.split(";") {
        if !stmt.trim().is_empty() {
            let mut conn = pool.get_conn().await?;
            conn.exec_drop(stmt, params::Params::Empty).await?;
        }
    }
    Ok(())
}

#[cfg(test)]
mod test {
    use std::error::Error;

    use derive_more::{Deref, DerefMut};

    use mysql_async::Pool;
    use testcontainers_modules::testcontainers::ContainerAsync;

    // This is a bit YOLO but is the quickest/cleanest way I can think of to a) get
    // a connection per database and b) ensure `Drop` cleans it up.
    #[derive(Deref, DerefMut)]
    struct TestPool(
        #[deref]
        #[deref_mut]
        Pool,
        // Never read, only needed for `Drop`.
        #[allow(dead_code)] ContainerAsync<testcontainers_modules::mysql::Mysql>,
    );

    impl TestPool {
        async fn try_new() -> Result<TestPool, Box<dyn Error>> {
            use testcontainers_modules::{mysql, testcontainers::runners::AsyncRunner};
            let mysql = mysql::Mysql::default().start().await?;
            let url = format!(
                "mysql://root@{}:{}/test",
                mysql.get_host().await?,
                mysql.get_host_port_ipv4(3306).await?
            );
            let pool = Pool::from_url(&url)?;
            Ok(TestPool(pool, mysql))
        }
    }

    #[tokio::test]
    async fn test_mysql() -> Result<(), Box<dyn std::error::Error>> {
        let pool = TestPool::try_new().await?;
        time_test::time_test!();
        crate::load_data(&pool).await?;
        <mysql_async::Pool as Clone>::clone(&pool)
            .disconnect()
            .await?;
        Ok(())
    }
}
