#![crate_name = "panda_logger"]
#![crate_type = "lib"]

//! # Logger - Rust 彩色日志库
//! 一个轻量级的 Rust 日志库，支持彩色输出、时间戳、文件和行号信息，
//! 以及灵活的配置选项。
//!
//! ## 特性
//! - 支持 7 个日志级别：Trace, Debug, Info, Warn, Error, Fatal, Custom
//! - 彩色输出，便于识别不同级别的日志
//! - 包含时间戳、线程名称、文件路径和行号信息
//! - 灵活的配置系统，支持：
//!   - 日志级别过滤
//!   - 路径显示方式（完整路径或仅文件名）
//!   - 自定义日期格式
//!   - 文件输出（控制台/文件/同时输出）
//! - 链式配置和运行时动态修改配置
//! - 单例模式设计，全局共享配置
//! - 零依赖（仅依赖 chrono 和 once_cell）
//!
//! ## 快速开始
//! ```rust
//! #[macro_use]
//! extern crate panda_logger;
//!
//! fn main() {
//!     panda_logger::Logger::get_instance().init();
//!     log_info!("程序启动");
//!     log_error!("发生错误");
//! }
//! ```
//!
//! ## 日志输出格式
//! ```text
//! 2026-04-25 23:22:24.128 [main] [I] logger\src\main.rs:22  这是 INFO 级别的日志
//! ```
//!
//! ### 日志元素说明
//!
//! | 元素 | 示例 | 说明 | 颜色 |
//! |------|------|------|------|
//! | 时间戳 | `2026-04-25 23:22:24.128` | 当前时间，可自定义格式 | 亮灰 |
//! | 线程名 | `[main]` | 线程名称，多线程时自动识别 | 哈希随机色 |
//! | 级别标识 | `[I]` | 日志级别 | 与消息同色 |
//! | 文件路径 | `logger\src\main.rs` | 调用日志的源文件 | 哈希随机色 |
//! | 行号 | `22` | 调用日志的代码行 | 亮灰 |
//! | 消息 | `这是 INFO 级别的日志` | 日志内容 | 与级别标识同色 |
//!
//! ### 级别标识颜色对照表
//!
//! | 级别 | 标识 | 宏 | 颜色 |
//! |------|------|-----|------|
//! | TRACE | `[T]` | `log_trace!(...)` | 亮灰 |
//! | DEBUG | `[D]` | `log_debug!(...)` | 亮蓝 |
//! | INFO | `[I]` | `log_info!(...)` | 亮绿 |
//! | WARN | `[W]` | `log_warn!(...)` | 亮黄 |
//! | ERROR | `[E]` | `log_error!(...)` | 亮紫 |
//! | FATAL | `[F]` | `log_fatal!(...)` | 亮红 |
//! | CUSTOM | `[C]` | `log_custom!(...)` | 亮青 |
//!
//! ## 可用宏
//!
//! | 宏 | 说明 | 颜色 |
//! |-----|------|------|
//! | `log_trace!(...)` | TRACE 级别，最详细调试信息 | 亮灰 |
//! | `log_debug!(...)` | DEBUG 级别，开发调试用 | 亮蓝 |
//! | `log_info!(...)` | INFO 级别，一般信息 | 亮绿 |
//! | `log_warn!(...)` | WARN 级别，警告信息 | 亮黄 |
//! | `log_error!(...)` | ERROR 级别，错误信息 | 亮紫 |
//! | `log_fatal!(...)` | FATAL 级别，严重错误 | 亮红 |
//! | `log_custom!(...)` | CUSTOM 级别，自定义日志 | 亮青 |
//!
//! ## 配置示例
//!
//! ### 链式配置（推荐）
//! ```rust
//! use panda_logger::{LogLevel, PathDisplay};
//!
//! fn main() {
//!     panda_logger::Logger::get_instance()
//!         .init()
//!         .set_level(LogLevel::Debug)
//!         .set_path_display(PathDisplay::Full)
//!         .set_date_format("%Y/%m/%d %H:%M:%S.%3f");
//! }
//! ```
//!
//! ### 运行时修改配置
//! ```rust
//! use panda_logger::{LogLevel, PathDisplay};
//!
//! fn main() {
//!     panda_logger::Logger::get_instance()
//!         .set_level(LogLevel::Trace)
//!         .set_path_display(PathDisplay::FileName)
//!         .set_date_format("%H:%M:%S.%3f");
//! }
//! ```
//!
//! ### 多线程日志
//! ```rust
//! #[macro_use]
//! extern crate panda_logger;
//! use panda_logger::Logger;
//!
//! fn main() {
//!     Logger::get_instance().init().set_level(panda_logger::LogLevel::Info);
//!
//!     let handle = std::thread::Builder::new()
//!         .name("worker-1".to_string())
//!         .spawn(|| {
//!             log_info!("这是在 worker-1 线程中的日志");
//!         })
//!         .unwrap();
//!
//!     handle.join().unwrap();
//! }
//! ```
//!
//! ### 文件输出
//! ```rust
//! #[macro_use]
//! extern crate panda_logger;
//! use panda_logger::{Logger, LogOutput};
//!
//! fn main() {
//!     // 配置为同时输出到控制台和文件
//!     Logger::get_instance()
//!         .init()
//!         .set_output(LogOutput::Both)
//!         .set_file_path("app.log")
//!         .set_level(panda_logger::LogLevel::Info);
//!
//!     log_info!("这条日志会同时输出到控制台和文件");
//!
//!     // 关闭文件写入
//!     Logger::get_instance().close();
//! }
//! ```

use once_cell::sync::Lazy;
use std::sync::Mutex;

// ==================== 日志颜色模块 ====================
// 该模块负责日志输出中各种元素的颜色渲染
// 使用 ANSI 转义序列实现彩色输出

/// 日志颜色定义模块
/// 包含各种日志级别的颜色常量和辅助函数
pub mod log_colors {
    use std::collections::HashMap;
    use std::sync::Mutex;
    use once_cell::sync::Lazy;

    static GRAY_ESCAPE: &str = "\x1b[38;2;128;128;128m";
    static RESET_ESCAPE: &str = "\x1b[0m";

    #[inline]
    pub fn gray(s: &str) -> String {
        format!("{}{}{}", GRAY_ESCAPE, s, RESET_ESCAPE)
    }

    #[inline]
    pub fn debug(s: &str) -> String {
        format!("\x1b[94m{}{}\x1b[0m", s, RESET_ESCAPE)
    }

    #[inline]
    pub fn info(s: &str) -> String {
        format!("\x1b[92m{}{}\x1b[0m", s, RESET_ESCAPE)
    }

    #[inline]
    pub fn warn(s: &str) -> String {
        format!("\x1b[93m{}{}\x1b[0m", s, RESET_ESCAPE)
    }

    #[inline]
    pub fn error(s: &str) -> String {
        format!("\x1b[95m{}{}\x1b[0m", s, RESET_ESCAPE)
    }

    #[inline]
    pub fn trace(s: &str) -> String {
        format!("\x1b[90m{}{}\x1b[0m", s, RESET_ESCAPE)
    }

    #[inline]
    pub fn fatal(s: &str) -> String {
        format!("\x1b[91m{}{}\x1b[0m", s, RESET_ESCAPE)
    }

    #[inline]
    pub fn custom(s: &str) -> String {
        format!("\x1b[96m{}{}\x1b[0m", s, RESET_ESCAPE)
    }

    static THREAD_CACHE: Lazy<Mutex<Option<(String, String)>>> = Lazy::new(|| Mutex::new(None));

    #[inline]
    pub fn thread_name() -> String {
        let mut cache = THREAD_CACHE.lock().unwrap();
        let thread = std::thread::current();
        let name = thread.name().unwrap_or("main");

        if let Some((ref cached_name, ref cached_color)) = *cache {
            if cached_name == name {
                return format!("{}{}{}", cached_color, name, RESET_ESCAPE);
            }
        }

        let hash: u32 = name.bytes().fold(0u32, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u32));
        let r = ((hash >> 16) & 0xFF) as u8;
        let g = ((hash >> 8) & 0xFF) as u8;
        let b = (hash & 0xFF) as u8;
        let color = format!("\x1b[38;2;{};{};{}m", r, g, b);
        *cache = Some((name.to_string(), color.clone()));
        format!("{}{}{}", color, name, RESET_ESCAPE)
    }

    #[inline]
    pub fn thread_name_plain() -> String {
        std::thread::current().name().unwrap_or("main").to_string()
    }

    static FILE_COLOR_CACHE: Lazy<Mutex<HashMap<String, String>>> = Lazy::new(|| Mutex::new(HashMap::new()));

    #[inline]
    pub fn random_color(file: &str) -> String {
        let mut cache = FILE_COLOR_CACHE.lock().unwrap();
        if let Some(cached) = cache.get(file) {
            return cached.clone();
        }
        let hash: u32 = file.bytes().fold(0u32, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u32));
        let r = ((hash >> 16) & 0xFF) as u8;
        let g = ((hash >> 8) & 0xFF) as u8;
        let b = (hash & 0xFF) as u8;
        let color = format!("\x1b[38;2;{};{};{}m", r, g, b);
        cache.insert(file.to_string(), color.clone());
        color
    }
}

/// 日志级别枚举
/// 定义了 7 个日志级别，按优先级从低到高排列：
/// Trace < Debug < Info < Warn < Error < Fatal < Custom
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
pub enum LogLevel {
    /// 最详细的日志级别，用于详细的调试信息
    Trace = 0,
    /// 调试级别，用于开发和调试过程中的信息
    Debug = 1,
    /// 信息级别，用于一般的运行时信息
    Info = 2,
    /// 警告级别，用于可能的问题
    Warn = 3,
    /// 错误级别，用于错误情况
    Error = 4,
    /// 致命级别，用于严重错误
    Fatal = 5,
    /// 自定义级别，不受日志级别限制
    Custom = 6,
}

/// 路径显示方式
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PathDisplay {
    /// 显示完整的文件路径
    Full,
    /// 仅显示文件名
    FileName,
}

impl Default for PathDisplay {
    fn default() -> Self {
        PathDisplay::FileName
    }
}

/// 日志输出目标
/// 控制日志的输出方式：仅控制台、仅文件、或两者同时
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LogOutput {
    /// 仅输出到控制台（默认）
    Console,
    /// 仅输出到文件
    File,
    /// 同时输出到控制台和文件
    Both,
}

impl Default for LogOutput {
    fn default() -> Self {
        LogOutput::Console
    }
}

/// 日志配置结构体
#[derive(Clone, Debug)]
pub struct LoggerConfig {
    pub min_level: LogLevel,
    pub path_display: PathDisplay,
    pub date_format: String,
    pub output: LogOutput,
    pub file_path: Option<String>,
}

impl Default for LoggerConfig {
    fn default() -> Self {
        LoggerConfig {
            min_level: LogLevel::Info,
            path_display: PathDisplay::FileName,
            date_format: "%Y-%m-%d %H:%M:%S.%3f".to_string(),
            output: LogOutput::Console,
            file_path: None,
        }
    }
}

impl LoggerConfig {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn level(mut self, level: LogLevel) -> Self {
        self.min_level = level;
        self
    }

    pub fn path_display(mut self, display: PathDisplay) -> Self {
        self.path_display = display;
        self
    }

    pub fn date_format(mut self, format: &str) -> Self {
        self.date_format = format.to_string();
        self
    }

    pub fn output(mut self, output: LogOutput) -> Self {
        self.output = output;
        self
    }

    pub fn file_path(mut self, path: &str) -> Self {
        self.file_path = Some(path.to_string());
        self
    }
}

/// Logger 单例结构体
pub struct Logger;

static LOGGER: Lazy<Logger> = Lazy::new(|| Logger);
static LOGGER_CONFIG: Lazy<Mutex<LoggerConfig>> = Lazy::new(|| Mutex::new(LoggerConfig::default()));
static FILE_WRITER: Lazy<Mutex<Option<std::fs::File>>> = Lazy::new(|| Mutex::new(None));

impl Logger {
    /// 获取 Logger 单例实例
    pub fn get_instance() -> &'static Logger {
        &LOGGER
    }

    fn get_config() -> LoggerConfig {
        LOGGER_CONFIG.lock().unwrap().clone()
    }

    fn set_config(config: LoggerConfig) {
        *LOGGER_CONFIG.lock().unwrap() = config;
    }

    #[allow(dead_code)]
    fn get_file_writer() -> std::sync::MutexGuard<'static, Option<std::fs::File>> {
        FILE_WRITER.lock().unwrap()
    }

    fn set_file_writer(file: std::fs::File) {
        *FILE_WRITER.lock().unwrap() = Some(file);
    }

    fn close_file_writer() {
        *FILE_WRITER.lock().unwrap() = None;
    }

    fn write_to_file(message: &str) {
        use std::io::Write;
        let mut writer = FILE_WRITER.lock().unwrap();
        if let Some(ref mut file) = *writer {
            let _ = file.write_all(message.as_bytes());
            let _ = file.write_all(b"\n");
            let _ = file.flush();
        }
    }

    /// 设置 UTF-8 编码
    fn set_utf8_encoding() {
        std::process::Command::new("powershell")
            .args([
                "-Command",
                "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8",
            ])
            .output()
            .ok();
        std::process::Command::new("cmd")
            .args(["/C", "chcp 65001"])
            .output()
            .ok();
    }

    /// 初始化日志系统（使用默认配置）
    pub fn init(&self) -> &Self {
        Self::set_utf8_encoding();
        Self::set_config(LoggerConfig::default());
        self
    }

    /// 使用自定义配置初始化
    pub fn init_with_config(&self, config: LoggerConfig) -> &Self {
        Self::set_utf8_encoding();
        Self::set_config(config.clone());
        if config.output != LogOutput::Console {
            if let Some(ref path) = config.file_path {
                if let Ok(file) = std::fs::OpenOptions::new()
                    .create(true)
                    .append(true)
                    .open(path)
                {
                    Self::set_file_writer(file);
                }
            }
        }
        self
    }

    /// 关闭文件写入器
    pub fn close(&self) {
        Self::close_file_writer();
    }

    /// 设置日志输出目标
    pub fn set_output(&self, output: LogOutput) -> &Self {
        let mut config = Self::get_config();
        config.output = output;
        Self::set_config(config);
        self
    }

    /// 设置日志文件路径
    pub fn set_file_path(&self, path: &str) -> &Self {
        let mut config = Self::get_config();
        config.file_path = Some(path.to_string());
        Self::set_config(config);
        if let Ok(file) = std::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open(path)
        {
            Self::set_file_writer(file);
        }
        self
    }

    /// 设置日志级别
    pub fn set_level(&self, level: LogLevel) -> &Self {
        let mut config = Self::get_config();
        config.min_level = level;
        Self::set_config(config);
        self
    }

    /// 设置路径显示方式
    pub fn set_path_display(&self, display: PathDisplay) -> &Self {
        let mut config = Self::get_config();
        config.path_display = display;
        Self::set_config(config);
        self
    }

    /// 设置日期格式
    pub fn set_date_format(&self, format: &str) -> &Self {
        let mut config = Self::get_config();
        config.date_format = format.to_string();
        Self::set_config(config);
        self
    }

    fn log_internal(level: LogLevel, args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        let config = Self::get_config();

        if level != LogLevel::Custom && level < config.min_level {
            return;
        }

        let now = chrono::Local::now();
        let timestamp = now.format(&config.date_format).to_string();

        let file_display: &str = match config.path_display {
            PathDisplay::Full => file,
            PathDisplay::FileName => {
                if let Some(pos) = file.rfind('/').or_else(|| file.rfind('\\')) {
                    &file[pos + 1..]
                } else {
                    file
                }
            }
        };

        let (level_color_fn, level_prefix): (fn(&str) -> String, &str) = match level {
            LogLevel::Trace => (log_colors::trace, "T"),
            LogLevel::Debug => (log_colors::debug, "D"),
            LogLevel::Info => (log_colors::info, "I"),
            LogLevel::Warn => (log_colors::warn, "W"),
            LogLevel::Error => (log_colors::error, "E"),
            LogLevel::Fatal => (log_colors::fatal, "F"),
            LogLevel::Custom => (log_colors::custom, "C"),
        };

        let level_prefix_str = level_color_fn(level_prefix);
        let file_color = log_colors::random_color(file_display);
        let line_str = format!("{:<4}", line);

        let message = format!(
            "{} [{}] [{}] {}{}:{}  {}",
            log_colors::gray(&timestamp),
            log_colors::thread_name(),
            level_prefix_str,
            file_color,
            file_display,
            log_colors::gray(&line_str),
            level_color_fn(&format!("{}", args))
        );

        let plain_message = format!(
            "{} [{}] [{}] {}:{}  {}",
            timestamp,
            log_colors::thread_name_plain(),
            level_prefix,
            file_display,
            line,
            args
        );

        match config.output {
            LogOutput::Console => {
                println!("{}", message);
            }
            LogOutput::File => {
                Self::write_to_file(&plain_message);
            }
            LogOutput::Both => {
                println!("{}", message);
                Self::write_to_file(&plain_message);
            }
        }
    }

    // ==================== 日志记录方法（宏调用） ====================
    // 注意：这些是静态方法，供宏调用使用
    // 宏会在调用点展开 file!() 和 line!()，因此能获得正确的调用位置
    // 实例方法（带 &self）已被移除，因为无法正确获取调用位置

    /// TRACE 级别日志 - 静态方法，供 log_trace! 宏调用
    pub fn trace_static(args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        Self::log_internal(LogLevel::Trace, args, file, line);
    }

    /// DEBUG 级别日志 - 静态方法，供 log_debug! 宏调用
    pub fn debug_static(args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        Self::log_internal(LogLevel::Debug, args, file, line);
    }

    /// INFO 级别日志 - 静态方法，供 log_info! 宏调用
    pub fn info_static(args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        Self::log_internal(LogLevel::Info, args, file, line);
    }

    /// WARN 级别日志 - 静态方法，供 log_warn! 宏调用
    pub fn warn_static(args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        Self::log_internal(LogLevel::Warn, args, file, line);
    }

    /// ERROR 级别日志 - 静态方法，供 log_error! 宏调用
    pub fn error_static(args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        Self::log_internal(LogLevel::Error, args, file, line);
    }

    /// FATAL 级别日志 - 静态方法，供 log_fatal! 宏调用
    pub fn fatal_static(args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        Self::log_internal(LogLevel::Fatal, args, file, line);
    }

    /// CUSTOM 级别日志 - 静态方法，供 log_custom! 宏调用
    pub fn custom_static(args: std::fmt::Arguments<'_>, file: &str, line: u32) {
        Self::log_internal(LogLevel::Custom, args, file, line);
    }
}

// ==================== 日志宏定义 ====================
// Rust 的宏系统在编译时展开，可以访问调用位置的 file!() 和 line!() 宏
// 这使得日志能够正确记录调用位置信息
// 使用方法：log_info!("消息 {}", value);

#[macro_export]
macro_rules! log_trace {
    ($($arg:tt)*) => { $crate::Logger::trace_static(format_args!($($arg)*), file!(), line!()); };
}

#[macro_export]
macro_rules! log_debug {
    ($($arg:tt)*) => { $crate::Logger::debug_static(format_args!($($arg)*), file!(), line!()); };
}

#[macro_export]
macro_rules! log_info {
    ($($arg:tt)*) => { $crate::Logger::info_static(format_args!($($arg)*), file!(), line!()); };
}

#[macro_export]
macro_rules! log_warn {
    ($($arg:tt)*) => { $crate::Logger::warn_static(format_args!($($arg)*), file!(), line!()); };
}

#[macro_export]
macro_rules! log_error {
    ($($arg:tt)*) => { $crate::Logger::error_static(format_args!($($arg)*), file!(), line!()); };
}

#[macro_export]
macro_rules! log_fatal {
    ($($arg:tt)*) => { $crate::Logger::fatal_static(format_args!($($arg)*), file!(), line!()); };
}

#[macro_export]
macro_rules! log_custom {
    ($($arg:tt)*) => { $crate::Logger::custom_static(format_args!($($arg)*), file!(), line!()); };
}

/// 通用的 log! 宏，支持动态指定日志级别
/// 使用方式：log!(info, "消息 {}", value);
#[macro_export]
macro_rules! log {
    (trace, $($arg:tt)*) => { $crate::log_trace!($($arg)*); };
    (debug, $($arg:tt)*) => { $crate::log_debug!($($arg)*); };
    (info, $($arg:tt)*) => { $crate::log_info!($($arg)*); };
    (warn, $($arg:tt)*) => { $crate::log_warn!($($arg)*); };
    (error, $($arg:tt)*) => { $crate::log_error!($($arg)*); };
    (fatal, $($arg:tt)*) => { $crate::log_fatal!($($arg)*); };
    (custom, $($arg:tt)*) => { $crate::log_custom!($($arg)*); };
}

mod timer;
pub use timer::{GlobalTimer, Timer, format_duration};
