use crate::sinks::Sink;
use crate::{
    sinks, AppConfig, AuditLogEntry, BaseLogEntry, ConsoleLogEntry, GlobalError, OtelConfig, ServerLogEntry, UnifiedLogEntry,
};
use rustfs_config::{APP_NAME, ENVIRONMENT, SERVICE_VERSION};
use std::sync::Arc;
use std::time::SystemTime;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::sync::{Mutex, OnceCell};
use tracing_core::Level;

// Add the global instance at the module level
static GLOBAL_LOGGER: OnceCell<Arc<Mutex<Logger>>> = OnceCell::const_new();

/// Server log processor
#[derive(Debug)]
pub struct Logger {
    sender: Sender<UnifiedLogEntry>, // Log sending channel
    queue_capacity: usize,
}

impl Logger {
    /// Create a new Logger instance
    /// Returns Logger and corresponding Receiver
    pub fn new(config: &AppConfig) -> (Self, Receiver<UnifiedLogEntry>) {
        // Get queue capacity from configuration, or use default values 10000
        let queue_capacity = config.logger.as_ref().and_then(|l| l.queue_capacity).unwrap_or(10000);
        let (sender, receiver) = mpsc::channel(queue_capacity);
        (Logger { sender, queue_capacity }, receiver)
    }

    /// get the queue capacity
    /// This function returns the queue capacity.
    /// # Returns
    /// The queue capacity
    /// # Example
    /// ```
    /// use rustfs_obs::Logger;
    /// async fn example(logger: &Logger) {
    ///    let _ = logger.get_queue_capacity();
    /// }
    /// ```
    pub fn get_queue_capacity(&self) -> usize {
        self.queue_capacity
    }

    /// Log a server entry
    #[tracing::instrument(skip(self), fields(log_source = "logger_server"))]
    pub async fn log_server_entry(&self, entry: ServerLogEntry) -> Result<(), GlobalError> {
        self.log_entry(UnifiedLogEntry::Server(entry)).await
    }

    /// Log an audit entry
    #[tracing::instrument(skip(self), fields(log_source = "logger_audit"))]
    pub async fn log_audit_entry(&self, entry: AuditLogEntry) -> Result<(), GlobalError> {
        self.log_entry(UnifiedLogEntry::Audit(Box::new(entry))).await
    }

    /// Log a console entry
    #[tracing::instrument(skip(self), fields(log_source = "logger_console"))]
    pub async fn log_console_entry(&self, entry: ConsoleLogEntry) -> Result<(), GlobalError> {
        self.log_entry(UnifiedLogEntry::Console(entry)).await
    }

    /// Asynchronous logging of unified log entries
    #[tracing::instrument(skip(self), fields(log_source = "logger"))]
    #[tracing::instrument(level = "error", skip_all)]
    pub async fn log_entry(&self, entry: UnifiedLogEntry) -> Result<(), GlobalError> {
        // Extract information for tracing based on entry type
        match &entry {
            UnifiedLogEntry::Server(server) => {
                tracing::Span::current()
                    .record("log_level", server.level.0.as_str())
                    .record("log_message", server.base.message.as_deref().unwrap_or("log message not set"))
                    .record("source", &server.source);

                // Generate tracing event based on log level
                match server.level.0 {
                    Level::ERROR => {
                        tracing::error!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or(""));
                    }
                    Level::WARN => {
                        tracing::warn!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or(""));
                    }
                    Level::INFO => {
                        tracing::info!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or(""));
                    }
                    Level::DEBUG => {
                        tracing::debug!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or(""));
                    }
                    Level::TRACE => {
                        tracing::trace!(target: "server_logs", message = %server.base.message.as_deref().unwrap_or(""));
                    }
                }
            }
            UnifiedLogEntry::Audit(audit) => {
                tracing::info!(
                    target: "audit_logs",
                    event = %audit.event,
                    api = %audit.api.name.as_deref().unwrap_or("unknown"),
                    message = %audit.base.message.as_deref().unwrap_or("")
                );
            }
            UnifiedLogEntry::Console(console) => {
                let level_str = match console.level {
                    crate::LogKind::Info => "INFO",
                    crate::LogKind::Warning => "WARN",
                    crate::LogKind::Error => "ERROR",
                    crate::LogKind::Fatal => "FATAL",
                };

                tracing::info!(
                    target: "console_logs",
                    level = %level_str,
                    node = %console.node_name,
                    message = %console.console_msg
                );
            }
        }

        // Send logs to async queue with improved error handling
        match self.sender.try_send(entry) {
            Ok(_) => Ok(()),
            Err(mpsc::error::TrySendError::Full(entry)) => {
                // Processing strategy when queue is full
                tracing::warn!("Log queue full, applying backpressure");
                match tokio::time::timeout(std::time::Duration::from_millis(500), self.sender.send(entry)).await {
                    Ok(Ok(_)) => Ok(()),
                    Ok(Err(_)) => Err(GlobalError::SendFailed("Channel closed")),
                    Err(_) => Err(GlobalError::Timeout("Queue backpressure timeout")),
                }
            }
            Err(mpsc::error::TrySendError::Closed(_)) => Err(GlobalError::SendFailed("Logger channel closed")),
        }
    }

    /// Write log with context information
    /// This function writes log messages with context information.
    ///
    /// # Parameters
    /// - `message`: Message to be logged
    /// - `source`: Source of the log
    /// - `request_id`: Request ID
    /// - `user_id`: User ID
    /// - `fields`: Additional fields
    ///
    /// # Returns
    /// Result indicating whether the operation was successful
    ///
    /// # Example
    /// ```
    /// use tracing_core::Level;
    /// use rustfs_obs::Logger;
    ///
    /// async fn example(logger: &Logger) {
    ///    let _ = logger.write_with_context("This is an information message", "example",Level::INFO, Some("req-12345".to_string()), Some("user-6789".to_string()), vec![("endpoint".to_string(), "/api/v1/data".to_string())]).await;
    /// }
    pub async fn write_with_context(
        &self,
        message: &str,
        source: &str,
        level: Level,
        request_id: Option<String>,
        user_id: Option<String>,
        fields: Vec<(String, String)>,
    ) -> Result<(), GlobalError> {
        let base = BaseLogEntry::new().message(Some(message.to_string())).request_id(request_id);

        let server_entry = ServerLogEntry::new(level, source.to_string())
            .user_id(user_id)
            .fields(fields)
            .with_base(base);

        self.log_server_entry(server_entry).await
    }

    /// Write log
    /// This function writes log messages.
    /// # Parameters
    /// - `message`: Message to be logged
    /// - `source`: Source of the log
    /// - `level`: Log level
    ///
    /// # Returns
    /// Result indicating whether the operation was successful
    ///
    /// # Example
    /// ```
    /// use rustfs_obs::Logger;
    /// use tracing_core::Level;
    ///
    /// async fn example(logger: &Logger) {
    ///   let _ = logger.write("This is an information message", "example", Level::INFO).await;
    /// }
    /// ```
    pub async fn write(&self, message: &str, source: &str, level: Level) -> Result<(), GlobalError> {
        self.write_with_context(message, source, level, None, None, Vec::new()).await
    }

    /// Shutdown the logger
    /// This function shuts down the logger.
    ///
    /// # Returns
    /// Result indicating whether the operation was successful
    ///
    /// # Example
    /// ```
    /// use rustfs_obs::Logger;
    ///
    /// async fn example(logger: Logger) {
    ///  let _ = logger.shutdown().await;
    /// }
    /// ```
    pub async fn shutdown(self) -> Result<(), GlobalError> {
        drop(self.sender); //Close the sending end so that the receiver knows that there is no new message
        Ok(())
    }
}

/// Start the log module
/// This function starts the log module.
/// It initializes the logger and starts the worker to process logs.
/// # Parameters
/// - `config`: Configuration information
/// - `sinks`: A vector of Sink instances
/// # Returns
/// The global logger instance
/// # Example
/// ```no_run
/// use rustfs_obs::{AppConfig, start_logger};
///
/// let config = AppConfig::default();
/// let sinks = vec![];
/// let logger = start_logger(&config, sinks);
/// ```
pub fn start_logger(config: &AppConfig, sinks: Vec<Arc<dyn Sink>>) -> Logger {
    let (logger, receiver) = Logger::new(config);
    tokio::spawn(crate::worker::start_worker(receiver, sinks));
    logger
}

/// Initialize the global logger instance
/// This function initializes the global logger instance and returns a reference to it.
/// If the logger has been initialized before, it will return the existing logger instance.
///
/// # Parameters
/// - `config`: Configuration information
/// - `sinks`: A vector of Sink instances
///
/// # Returns
/// A reference to the global logger instance
///
/// # Example
/// ```
/// use rustfs_obs::{AppConfig,init_global_logger};
///
/// let config = AppConfig::default();
/// let logger = init_global_logger(&config);
/// ```
pub async fn init_global_logger(config: &AppConfig) -> Arc<Mutex<Logger>> {
    let sinks = sinks::create_sinks(config).await;
    let logger = Arc::new(Mutex::new(start_logger(config, sinks)));
    GLOBAL_LOGGER.set(logger.clone()).expect("Logger already initialized");
    logger
}

/// Get the global logger instance
///
/// This function returns a reference to the global logger instance.
///
/// # Returns
/// A reference to the global logger instance
///
/// # Example
/// ```no_run
/// use rustfs_obs::get_global_logger;
///
/// let logger = get_global_logger();
/// ```
pub fn get_global_logger() -> &'static Arc<Mutex<Logger>> {
    GLOBAL_LOGGER.get().expect("Logger not initialized")
}

/// Log information
/// This function logs information messages.
///
/// # Parameters
/// - `message`: Message to be logged
/// - `source`: Source of the log
///
/// # Returns
/// Result indicating whether the operation was successful
///
/// # Example
/// ```no_run
/// use rustfs_obs::log_info;
///
/// async fn example() {
///    let _ = log_info("This is an information message", "example").await;
/// }
/// ```
pub async fn log_info(message: &str, source: &str) -> Result<(), GlobalError> {
    get_global_logger().lock().await.write(message, source, Level::INFO).await
}

/// Log error
/// This function logs error messages.
/// # Parameters
/// - `message`: Message to be logged
/// - `source`: Source of the log
/// # Returns
/// Result indicating whether the operation was successful
/// # Example
/// ```no_run
/// use rustfs_obs::log_error;
///
/// async fn example() {
///     let _ = log_error("This is an error message", "example").await;
/// }
pub async fn log_error(message: &str, source: &str) -> Result<(), GlobalError> {
    get_global_logger().lock().await.write(message, source, Level::ERROR).await
}

/// Log warning
/// This function logs warning messages.
/// # Parameters
/// - `message`: Message to be logged
/// - `source`: Source of the log
/// # Returns
/// Result indicating whether the operation was successful
///
/// # Example
/// ```no_run
/// use rustfs_obs::log_warn;
///
/// async fn example() {
///     let _ = log_warn("This is a warning message", "example").await;
/// }
/// ```
pub async fn log_warn(message: &str, source: &str) -> Result<(), GlobalError> {
    get_global_logger().lock().await.write(message, source, Level::WARN).await
}

/// Log debug
/// This function logs debug messages.
/// # Parameters
/// - `message`: Message to be logged
/// - `source`: Source of the log
/// # Returns
/// Result indicating whether the operation was successful
///
/// # Example
/// ```no_run
/// use rustfs_obs::log_debug;
///
/// async fn example() {
///     let _ = log_debug("This is a debug message", "example").await;
/// }
/// ```
pub async fn log_debug(message: &str, source: &str) -> Result<(), GlobalError> {
    get_global_logger().lock().await.write(message, source, Level::DEBUG).await
}

/// Log trace
/// This function logs trace messages.
/// # Parameters
/// - `message`: Message to be logged
/// - `source`: Source of the log
///
/// # Returns
/// Result indicating whether the operation was successful
///
/// # Example
/// ```no_run
/// use rustfs_obs::log_trace;
///
/// async fn example() {
///    let _ = log_trace("This is a trace message", "example").await;
/// }
/// ```
pub async fn log_trace(message: &str, source: &str) -> Result<(), GlobalError> {
    get_global_logger().lock().await.write(message, source, Level::TRACE).await
}

/// Log with context information
/// This function logs messages with context information.
/// # Parameters
/// - `message`: Message to be logged
/// - `source`: Source of the log
/// - `level`: Log level
/// - `request_id`: Request ID
/// - `user_id`: User ID
/// - `fields`: Additional fields
/// # Returns
/// Result indicating whether the operation was successful
/// # Example
/// ```no_run
/// use tracing_core::Level;
/// use rustfs_obs::log_with_context;
///
/// async fn example() {
///    let _ = log_with_context("This is an information message", "example", Level::INFO, Some("req-12345".to_string()), Some("user-6789".to_string()), vec![("endpoint".to_string(), "/api/v1/data".to_string())]).await;
/// }
/// ```
pub async fn log_with_context(
    message: &str,
    source: &str,
    level: Level,
    request_id: Option<String>,
    user_id: Option<String>,
    fields: Vec<(String, String)>,
) -> Result<(), GlobalError> {
    get_global_logger()
        .lock()
        .await
        .write_with_context(message, source, level, request_id, user_id, fields)
        .await
}

/// Log initialization status
#[derive(Debug)]
pub(crate) struct InitLogStatus {
    pub timestamp: SystemTime,
    pub service_name: String,
    pub version: String,
    pub environment: String,
}

impl Default for InitLogStatus {
    fn default() -> Self {
        Self {
            timestamp: SystemTime::now(),
            service_name: String::from(APP_NAME),
            version: SERVICE_VERSION.to_string(),
            environment: ENVIRONMENT.to_string(),
        }
    }
}

impl InitLogStatus {
    pub fn new_config(config: &OtelConfig) -> Self {
        let config = config.clone();
        let environment = config.environment.unwrap_or(ENVIRONMENT.to_string());
        let version = config.service_version.unwrap_or(SERVICE_VERSION.to_string());
        Self {
            timestamp: SystemTime::now(),
            service_name: String::from(APP_NAME),
            version,
            environment,
        }
    }

    pub async fn init_start_log(config: &OtelConfig) -> Result<(), GlobalError> {
        let status = Self::new_config(config);
        log_init_state(Some(status)).await
    }
}

/// Log initialization details during system startup
async fn log_init_state(status: Option<InitLogStatus>) -> Result<(), GlobalError> {
    let status = status.unwrap_or_default();

    let base_entry = BaseLogEntry::new()
        .timestamp(chrono::DateTime::from(status.timestamp))
        .message(Some(format!(
            "Service initialization started - {} v{} in {}",
            status.service_name, status.version, status.environment
        )))
        .request_id(Some("system_init".to_string()));

    let server_entry = ServerLogEntry::new(Level::INFO, "system_initialization".to_string())
        .with_base(base_entry)
        .user_id(Some("system".to_string()));

    get_global_logger().lock().await.log_server_entry(server_entry).await?;
    Ok(())
}
