use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::fmt::Display;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
        Trace = 0,
        Debug = 1,
        Info = 2,
        Warn = 3,
        Error = 4,
        Critical = 5,
}
impl LogLevel {
        pub fn as_str(&self) -> &'static str {
        match self {
            LogLevel::Trace => "TRACE",
            LogLevel::Debug => "DEBUG",
            LogLevel::Info => "INFO",
            LogLevel::Warn => "WARN",
            LogLevel::Error => "ERROR",
            LogLevel::Critical => "CRITICAL",
        }
    }
}
#[derive(Debug, Clone)]
pub struct LogEntry {
        pub timestamp: std::time::SystemTime,
        pub level: LogLevel,
        pub module: String,
        pub message: String,
        pub fields: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct LoggerConfig {
        pub min_level: LogLevel,
        pub show_timestamps: bool,
        pub show_modules: bool,
        pub module_levels: HashMap<String, LogLevel>,
}
impl Default for LoggerConfig {
    fn default() -> Self {
        Self {
            min_level: LogLevel::Info,
            show_timestamps: true,
            show_modules: true,
            module_levels: HashMap::new(),
        }
    }
}
static LOGGER_CONFIG: Lazy<Mutex<LoggerConfig>> = Lazy::new(|| Mutex::new(LoggerConfig::default()));
pub fn configure_logger(config: LoggerConfig) {
    let mut global_config = LOGGER_CONFIG.lock().unwrap();
    *global_config = config;
}
pub fn set_min_log_level(level: LogLevel) {
    let mut config = LOGGER_CONFIG.lock().unwrap();
    config.min_level = level;
}
pub fn set_module_log_level(module: &str, level: LogLevel) {
    let mut config = LOGGER_CONFIG.lock().unwrap();
    config.module_levels.insert(module.to_string(), level);
}
pub trait LogHandler: Send + Sync {
        fn handle(&self, entry: &LogEntry);
}
pub struct ConsoleLogHandler {
        pub format: String,
}
impl Default for ConsoleLogHandler {
    fn default() -> Self {
        Self {
            format: "[{level}] {module}: {message}".to_string(),
        }
    }
}
impl LogHandler for ConsoleLogHandler {
    fn handle(&self, entry: &LogEntry) {
        let mut output = self.format.clone();
                output = output.replace("{level}", entry.level.as_str());
        output = output.replace("{module}", &entry.module);
        output = output.replace("{message}", &entry.message);
        if self.format.contains("{timestamp}") {
            let datetime = chrono::DateTime::<chrono::Utc>::from(entry.timestamp);
            output = output.replace(
                "{timestamp}",
                &datetime.format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
            );
        }
        if self.format.contains("{fields}") {
            let fields_str = entry
                .fields
                .iter()
                .map(|(k, v)| format!("{}={}", k, v))
                .collect::<Vec<_>>()
                .join(", ");
            output = output.replace("{fields}", &fields_str);
        }
                match entry.level {
            LogLevel::Error | LogLevel::Critical => eprintln!("{}", output),
            _ => println!("{}", output),
        }
    }
}
pub struct FileLogHandler {
        pub file_path: String,
        pub format: String,
}
impl LogHandler for FileLogHandler {
    fn handle(&self, entry: &LogEntry) {
                
        let mut output = self.format.clone();
                output = output.replace("{level}", entry.level.as_str());
        output = output.replace("{module}", &entry.module);
        output = output.replace("{message}", &entry.message);
        if self.format.contains("{timestamp}") {
            let datetime = chrono::DateTime::<chrono::Utc>::from(entry.timestamp);
            output = output.replace(
                "{timestamp}",
                &datetime.format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
            );
        }
        if self.format.contains("{fields}") {
            let fields_str = entry
                .fields
                .iter()
                .map(|(k, v)| format!("{}={}", k, v))
                .collect::<Vec<_>>()
                .join(", ");
            output = output.replace("{fields}", &fields_str);
        }
                        if let Ok(mut file) = std::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open(&self.file_path)
        {
            use std::io::Write;
            let _ = writeln!(file, "{}", output);
        }
    }
}
static LOG_HANDLERS: Lazy<Mutex<Vec<Arc<dyn LogHandler>>>> = Lazy::new(|| {
    let console_handler = Arc::new(ConsoleLogHandler::default());
    Mutex::new(vec![console_handler])
});
pub fn register_log_handler(handler: Arc<dyn LogHandler>) {
    let mut handlers = LOG_HANDLERS.lock().unwrap();
    handlers.push(handler);
}
pub fn clear_log_handlers() {
    let mut handlers = LOG_HANDLERS.lock().unwrap();
    handlers.clear();
}
pub fn reset_log_handlers() {
    let mut handlers = LOG_HANDLERS.lock().unwrap();
    handlers.clear();
    handlers.push(Arc::new(ConsoleLogHandler::default()));
}
#[derive(Clone)]
pub struct Logger {
        module: String,
        fields: HashMap<String, String>,
}
impl Logger {
        pub fn new(module: &str) -> Self {
        Self {
            module: module.to_string(),
            fields: HashMap::new(),
        }
    }
        pub fn with_field<K, V>(mut self, key: K, value: V) -> Self
    where
        K: Into<String>,
        V: Display,
    {
        self.fields.insert(key.into(), format!("{}", value));
        self
    }
        pub fn with_fields<K, V, I>(mut self, fields: I) -> Self
    where
        K: Into<String>,
        V: Display,
        I: IntoIterator<Item = (K, V)>,
    {
        for (key, value) in fields {
            self.fields.insert(key.into(), format!("{}", value));
        }
        self
    }
        pub fn log(&self, level: LogLevel, message: &str) {
                let config = LOGGER_CONFIG.lock().unwrap();
        let module_level = config
            .module_levels
            .get(&self.module)
            .copied()
            .unwrap_or(config.min_level);
        if level < module_level {
            return;
        }
                let entry = LogEntry {
            timestamp: std::time::SystemTime::now(),
            level,
            module: self.module.clone(),
            message: message.to_string(),
            fields: self.fields.clone(),
        };
                let handlers = LOG_HANDLERS.lock().unwrap();
        for handler in handlers.iter() {
            handler.handle(&entry);
        }
    }
        pub fn trace(&self, message: &str) {
        self.log(LogLevel::Trace, message);
    }
        pub fn debug(&self, message: &str) {
        self.log(LogLevel::Debug, message);
    }
        pub fn info(&self, message: &str) {
        self.log(LogLevel::Info, message);
    }
        pub fn warn(&self, message: &str) {
        self.log(LogLevel::Warn, message);
    }
        pub fn error(&self, message: &str) {
        self.log(LogLevel::Error, message);
    }
        pub fn critical(&self, message: &str) {
        self.log(LogLevel::Critical, message);
    }
}
pub struct ProgressTracker {
        name: String,
        total: usize,
        current: usize,
        start_time: Instant,
        last_update: Instant,
        update_interval: Duration,
        logger: Logger,
}
impl ProgressTracker {
        pub fn new(name: &str, total: usize) -> Self {
        let now = Instant::now();
        let logger = Logger::new("progress").with_field("operation", name);
        logger.info(&format!("Starting operation: {}", name));
        Self {
            name: name.to_string(),
            total,
            current: 0,
            start_time: now,
            last_update: now,
            update_interval: Duration::from_millis(500),             logger,
        }
    }
        pub fn set_update_interval(&mut self, interval: Duration) {
        self.update_interval = interval;
    }
        pub fn update(&mut self, current: usize) {
        self.current = current;
        let now = Instant::now();
                if now.duration_since(self.last_update) >= self.update_interval {
            self.last_update = now;
            let elapsed = now.duration_since(self.start_time);
            let percent = (self.current as f64 / self.total as f64) * 100.0;
            let eta = if self.current > 0 {
                let time_per_item = elapsed.as_secs_f64() / self.current as f64;
                let remaining = time_per_item * (self.total - self.current) as f64;
                format!("ETA: {:.1}s", remaining)
            } else {
                "ETA: calculating...".to_string()
            };
            self.logger.debug(&format!(
                "{}: {}/{} ({:.1}%) - Elapsed: {:.1}s - {}",
                self.name,
                self.current,
                self.total,
                percent,
                elapsed.as_secs_f64(),
                eta
            ));
        }
    }
        pub fn complete(&mut self) {
        let elapsed = self.start_time.elapsed();
        self.current = self.total;
        self.logger.info(&format!(
            "{} completed: {}/{} (100%) - Total time: {:.1}s",
            self.name,
            self.total,
            self.total,
            elapsed.as_secs_f64()
        ));
    }
        pub fn progress_percent(&self) -> f64 {
        (self.current as f64 / self.total as f64) * 100.0
    }
        pub fn elapsed(&self) -> Duration {
        self.start_time.elapsed()
    }
        pub fn eta(&self) -> Option<Duration> {
        if self.current == 0 {
            return None;
        }
        let elapsed = self.start_time.elapsed();
        let time_per_item = elapsed.as_secs_f64() / self.current as f64;
        let remaining_secs = time_per_item * (self.total - self.current) as f64;
        Some(Duration::from_secs_f64(remaining_secs))
    }
}
pub fn init() {
        let handlers = LOG_HANDLERS.lock().unwrap();
    if handlers.is_empty() {
        drop(handlers);
        reset_log_handlers();
    }
}
#[macro_export]
macro_rules! get_logger {
    () => {
        $crate::logging::Logger::new(module_path!())
    };
    ($name:expr) => {
        $crate::logging::Logger::new($name)
    };
}