// Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: MIT

use crate::constants::LOG_DEFAULT_LEVEL;
use crate::format;
use crate::memory::alloc::boxed::Box;
use crate::memory::alloc::string::String;
use crate::memory::alloc::vec::Vec;
use lazy_static::lazy_static;
use spin::Mutex;

pub type LogDevice = dyn Fn(&str) + Send + Sync;

pub struct Logger {
    pub level: LogLevel,
    devices: Vec<Box<LogDevice>>,
}

lazy_static! {
    pub static ref LOGGER: Mutex<Logger> = Mutex::new(Logger::new());
}

#[macro_export]
macro_rules! log_info {
    ($($arg:tt)*) => {
        LOGGER.lock().log(LogLevel::Info, &format!($($arg)*), file!(), line!(), column!())
    };
}

#[macro_export]
macro_rules! log_trace {
    ($($arg:tt)*) => {
        LOGGER.lock().log(LogLevel::Trace, &format!($($arg)*), file!(), line!(), column!())
    };
}

#[macro_export]
macro_rules! log_warning {
    ($($arg:tt)*) => {
        LOGGER.lock().log(LogLevel::Warning, &format!($($arg)*), file!(), line!(), column!())
    };
}

#[macro_export]
macro_rules! log_error {
    ($($arg:tt)*) => {
        LOGGER.lock().log(LogLevel::Error, &format!($($arg)*), file!(), line!(), column!())
    };
}

#[macro_export]
macro_rules! log_critical {
    ($($arg:tt)*) => {
        LOGGER.lock().log(LogLevel::Critical, &format!($($arg)*), file!(), line!(), column!())
    };
}

impl Logger {
    pub fn new() -> Self {
        Logger {
            level: LOG_DEFAULT_LEVEL,
            devices: Vec::new(),
        }
    }
    pub fn log(
        &mut self,
        level: LogLevel,
        message: &str,
        file: &'static str,
        line: u32,
        column: u32,
    ) {
        if level <= self.level {
            for dev in &self.devices {
                let message = format!(
                    "{} {}:{},{}- {}",
                    level.normalized_string(),
                    file,
                    line,
                    column,
                    message
                );
                dev(&message)
            }
        }
    }
    pub fn add_device(&mut self, device: Box<dyn Fn(&str) + Send + Sync>) {
        self.devices.push(device);
    }
}
pub struct EnumParseError {}

#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum LogLevel {
    Disabled = 0,
    Critical = 1,
    Error = 2,
    Warning = 3,
    Info = 4,
    Trace = 5,
}

impl TryFrom<u8> for LogLevel {
    type Error = EnumParseError;
    fn try_from(from: u8) -> Result<Self, <crate::log::LogLevel as TryFrom<u8>>::Error> {
        match from {
            0 => Ok(Self::Disabled),
            1 => Ok(Self::Critical),
            2 => Ok(Self::Error),
            3 => Ok(Self::Warning),
            4 => Ok(Self::Info),
            5 => Ok(Self::Trace),
            _ => Err(EnumParseError {}),
        }
    }
}

impl TryFrom<&str> for LogLevel {
    type Error = EnumParseError;
    fn try_from(from: &str) -> Result<Self, <crate::log::LogLevel as TryFrom<u8>>::Error> {
        match from.to_ascii_lowercase().as_ref() {
            "disabled" => Ok(Self::Disabled),
            "critical" => Ok(Self::Critical),
            "error" => Ok(Self::Error),
            "warning" => Ok(Self::Warning),
            "info" => Ok(Self::Info),
            "trace" => Ok(Self::Trace),
            _ => Err(EnumParseError {}),
        }
    }
}

impl From<LogLevel> for String {
    fn from(from: LogLevel) -> String {
        match from {
            LogLevel::Disabled => "Disabled",
            LogLevel::Critical => "CRIT ",
            LogLevel::Error => "ERROR",
            LogLevel::Warning => "WARN ",
            LogLevel::Info => "INFO ",
            LogLevel::Trace => "TRACE",
        }
        .into()
    }
}

impl LogLevel {
    fn normalized_string(&self) -> &str {
        match self {
            LogLevel::Critical => "[CRIT ]",
            LogLevel::Error => "[ERROR]",
            LogLevel::Info => "[INFO ]",
            LogLevel::Trace => "[TRACE]",
            LogLevel::Warning => "[WARN ]",
            LogLevel::Disabled => "[DSBLD]",
        }
    }
}
