use tracing::{info, trace, debug, warn, error, Span};
use crate::Tracer;

unsafe impl Send for Trace {}
unsafe impl Sync for Trace {}

pub struct Trace {
    pub name: String,
    is_silent: bool,
    pub trace_span: Span,
    pub info_span: Span,
    pub debug_span: Span,
    pub warn_span: Span,
    pub error_span: Span,
}

impl std::fmt::Debug for Trace {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        return f.debug_struct("Trace")
            .field("name", &self.name)
            .finish();
    }
}

// impl Default for Trace {
//     fn default() -> Trace {
//         return Trace::from("default");
//     }
// }

impl Tracer for Trace {
    fn name(&self) -> &str {
        return &self.name;
    }

    // fn child(&self, child_name: &'static str) -> Box<dyn Tracer> {

    //     unsafe { NAME1 = "child_name"; }

    //     let child_trace = Trace {
    //         is_silent: self.is_silent,
    //         trace_span: trace_span!(parent: &self.trace_span, "aaa"),
    //         info_span: info_span!(parent: &self.info_span, unsafe { NAME1 }),
    //         debug_span: debug_span!(parent: &self.debug_span, unsafe { NAME1 }),
    //         warn_span: warn_span!(parent: &self.warn_span, unsafe { NAME1 }),
    //         error_span: error_span!(parent: &self.error_span, unsafe { NAME1 }),
    //         name: child_name.to_string(),
    //     };

    //     return Box::new(
    //         child_trace.as_silent(self.is_silent),
    //     );
    // }

    fn trace(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        let _enter = self.trace_span.enter();
        trace!(message);
        
        return self;
    }

    fn debug(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        let _enter = self.debug_span.enter();
        debug!(message);

        return self;
    }

    fn info(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        let _enter = self.info_span.enter();
        info!(message);

        return self;
    }

    fn warn(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        let _enter = self.warn_span.enter();
        warn!(message);

        return self;
    }

    fn error(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        let _enter = self.error_span.enter();
        error!(message);

        return self;
    }
}

impl Trace {
    pub fn as_silent(mut self, is_silent: bool) -> Self {
        self.is_silent = is_silent;

        return self;
    }

    pub fn name(&self) -> &str {
        return Tracer::name(self);
    }

    // pub fn child(&self, child_name: &'static str) -> Box<dyn Tracer> {
    //     return Tracer::child(self, child_name);
    // }

    pub fn trace(&self, message: &str) -> &dyn Tracer {
        return Tracer::trace(self, message);
    }

    pub fn debug(&self, message: &str) -> &dyn Tracer {
        return Tracer::debug(self, message);
    }

    pub fn info(&self, message: &str) -> &dyn Tracer {
        return Tracer::info(self, message);

    }

    pub fn warn(&self, message: &str) -> &dyn Tracer {
        return Tracer::warn(self, message);

    }

    pub fn error(&self, message: &str) -> &dyn Tracer {
        return Tracer::error(self, message);

    }

    pub fn new(
        name: String,
        is_silent: bool,
        trace_span: Span,
        info_span: Span,
        debug_span: Span,
        warn_span: Span,
        error_span: Span,
    ) -> Self {
        return Trace {
            is_silent,
            trace_span,
            info_span,
            debug_span,
            warn_span,
            error_span,
            name,
        };
    }
}

#[macro_export]
macro_rules! create_trace {
    ($name:expr) => {
        Trace::new(
            ($name).to_string(),
            false,
            tracing::trace_span!($name),
            tracing::info_span!($name),
            tracing::debug_span!($name),
            tracing::warn_span!($name),
            tracing::error_span!($name),
        );
    };
}

#[macro_export]
macro_rules! child {
    ($parent_trace:ident, $name:expr) => {
        Trace::new(
            ($name).to_string(),
            false,
            tracing::trace_span!(parent: &$parent_trace.trace_span, $name),
            tracing::info_span!(parent: &$parent_trace.info_span, $name),
            tracing::debug_span!(parent: &$parent_trace.debug_span, $name),
            tracing::warn_span!(parent: &$parent_trace.warn_span, $name),
            tracing::error_span!(parent: &$parent_trace.error_span, $name),
        );
    };
}
