use std::fs;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};

use log_easy::{LogLevel, Logger, ParseLogLevelError};

fn temp_log_path(name: &str) -> PathBuf {
    let unique = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("system time should be after Unix epoch")
        .as_nanos();

    std::env::temp_dir().join(format!(
        "log-easy-{name}-{}-{unique}.log",
        std::process::id()
    ))
}

#[test]
fn parses_log_levels_from_names_and_aliases() {
    assert_eq!("trace".parse::<LogLevel>().unwrap(), LogLevel::Trace);
    assert_eq!("DEBUG".parse::<LogLevel>().unwrap(), LogLevel::Debug);
    assert_eq!("info".parse::<LogLevel>().unwrap(), LogLevel::Info);
    assert_eq!("warning".parse::<LogLevel>().unwrap(), LogLevel::Warn);
    assert_eq!("error".parse::<LogLevel>().unwrap(), LogLevel::Error);
    assert_eq!("5".parse::<LogLevel>().unwrap(), LogLevel::Trace);
    assert_eq!("4".parse::<LogLevel>().unwrap(), LogLevel::Debug);
    assert_eq!("3".parse::<LogLevel>().unwrap(), LogLevel::Info);
    assert_eq!("2".parse::<LogLevel>().unwrap(), LogLevel::Warn);
    assert_eq!("1".parse::<LogLevel>().unwrap(), LogLevel::Error);
}

#[test]
fn invalid_log_level_returns_exported_error() {
    let error: ParseLogLevelError = "verbose".parse::<LogLevel>().unwrap_err();

    assert_eq!(
        error.to_string(),
        "invalid log level 'verbose'. Expected one of: ERROR, WARN, INFO, DEBUG, TRACE"
    );
}

#[test]
fn log_level_display_uses_uppercase_level_name() {
    assert_eq!(LogLevel::Warn.to_string(), "WARN");
}

#[test]
fn enabled_reports_filtering_decision() {
    let logger = Logger::new("unused.log").with_level(LogLevel::Warn);

    assert!(!logger.enabled(LogLevel::Info));
    assert!(logger.enabled(LogLevel::Warn));
    assert!(logger.enabled(LogLevel::Error));
}

#[test]
fn try_logging_filters_and_formats_lines() {
    let path = temp_log_path("format");
    let logger = Logger::new(&path).with_level(LogLevel::Warn);

    logger.try_info("hidden").unwrap();
    logger.try_warn("visible").unwrap();

    let contents = fs::read_to_string(&path).unwrap();
    fs::remove_file(&path).unwrap();

    assert!(!contents.contains("hidden"));
    assert!(contents.contains("][WARN] visible\n"));
    assert_eq!(contents.lines().count(), 1);

    let line = contents.trim_end();
    assert!(line.starts_with('['));
    assert_eq!(line.chars().nth(20), Some(']'));
    assert_eq!(line.chars().nth(21), Some('['));
}
