use log::LevelFilter;
use once_cell::sync::Lazy;

use super::{interface::LogInspector, test_logger::TestLogger};
use std::sync::{Arc, Once, RwLock};

static TEST_LOGGER: TestLogger = TestLogger {
    lines: Lazy::new(|| Arc::new(RwLock::new(Vec::new()))),
};
static TEST_LOGGER_INIT: Once = Once::new();

/// Sets up the test logger and registers it with the log library.
/// Initialisation is lazy and will not be repeated in the same execution context.
/// This may lead to unexpected behaviour as the test logger is not reset between tests.
/// This is a [known issue](https://gitlab.com/pfouilloux/assertx/-/issues/4) and will be fixed in a future release (Pull requests welcome!)
///
pub fn setup_logging_test() -> Arc<dyn LogInspector> {
    TEST_LOGGER_INIT.call_once(|| {
        log::set_logger(&TEST_LOGGER)
            .expect("Failed to set logger. This is usually due to the logger already being set.");
        log::set_max_level(LevelFilter::Trace);
    });

    TEST_LOGGER.lines.clone()
}

#[cfg(test)]
mod tests {
    use super::setup_logging_test;
    use crate::assert_logs_contain_in_order;
    use log::{debug, error, info, trace, warn, Level};

    #[test]
    fn should_succeed_when_log_contains_error() {
        let logs = setup_logging_test();

        error!("Some error");

        assert_logs_contain_in_order!(logs, Level::Error =>  "Some error");
    }

    #[test]
    fn should_succeed_when_log_contains_warning() {
        let logs = setup_logging_test();

        warn!("Some warning");

        assert_logs_contain_in_order!(logs, Level::Warn =>  "Some warning");
    }

    #[test]
    fn should_succeed_when_log_contains_info() {
        let logs = setup_logging_test();

        info!("Some info");

        assert_logs_contain_in_order!(logs, Level::Info =>  "Some info");
    }

    #[test]
    fn should_succeed_when_log_contains_debug() {
        let logs = setup_logging_test();

        debug!("Some debug");

        assert_logs_contain_in_order!(logs, Level::Debug =>  "Some debug");
    }

    #[test]
    fn should_succeed_when_log_contains_trace() {
        let logs = setup_logging_test();

        trace!("Some trace");

        assert_logs_contain_in_order!(logs, Level::Trace =>  "Some trace");
    }
}
