use log::{Level, Record};
use log4rs::{
    config::{Deserialize, Deserializers},
    filter::{Filter, Response},
};

#[derive(serde::Deserialize)]
pub struct TargetFilterConfig {
    negation: bool,
    level: Vec<String>,
}

#[derive(Debug)]
pub struct TargetFilter {
    level: Vec<Level>,
    negation: bool,
}

impl TargetFilter {
    pub fn new(level: Vec<Level>, negation: bool) -> TargetFilter {
        TargetFilter { level, negation }
    }
}

impl Filter for TargetFilter {
    fn filter(&self, record: &Record) -> Response {
        if self.level.contains(&record.level()) {
            // Always allow error messages
            if !self.negation {
                Response::Accept
            } else {
                Response::Reject
            }
        } else {
            if self.negation {
                Response::Accept
            } else {
                Response::Reject
            }
        }
    }
}

pub struct TargetFilterDeserializer;

impl Deserialize for TargetFilterDeserializer {
    type Trait = dyn Filter;

    type Config = TargetFilterConfig;

    fn deserialize(
        &self,
        config: TargetFilterConfig,
        _: &Deserializers,
    ) -> anyhow::Result<Box<Self::Trait>> {
        Ok(Box::new(TargetFilter::new(
            config
                .level
                .iter()
                .map(|lvl| match lvl.to_lowercase().as_str() {
                    "error" => Level::Error,
                    "warn" => Level::Warn,
                    "info" => Level::Info,
                    "debug" => Level::Debug,
                    "trace" => Level::Trace,
                    _ => panic!("Unknown log level {}", lvl), // Default to Off if level is not recognized
                })
                .collect(),
            config.negation,
        )))
    }
}

pub fn initialize_logging(log_file: &str) -> Result<(), anyhow::Error> {
    let mut target_filter = Deserializers::new();
    target_filter.insert("target_filter", TargetFilterDeserializer);

    Ok(log4rs::init_file(log_file, target_filter)?)
}
