Documentation
use std::path::Path;

use crate::Result;
use chrono::{DateTime, Datelike as _, Local, Timelike as _};
use once_cell::sync::Lazy;
use regex::Regex;

/// 解析格式化字符串
pub trait MyParseFormat {
  /// 解析所有
  fn parse_all(&self) -> Result<String>;
  /// 解析特殊关键词
  fn parse_format(&self) -> Result<String>;
  /// 解析自定义关键词
  fn parse_replace<F>(&self, start: char, end: char, match_value: F) -> Result<String>
  where
    F: Fn(String) -> String;
  /// 解析含中文自定义关键词
  fn parse_replace_zh<F>(&self, start: char, end: char, match_value: F) -> Result<String>
  where
    F: Fn(String) -> String;
  /// 解析系统环境变量
  /// 解析跨平台系统环境变量(windows、linux、mac等)
  fn parse_env(&self) -> Result<String>;
  /// 解析路径规范,统一'/'
  fn parse_path(&self) -> String;
}

/// 正则表达式,用于匹配格式化字符串中的占位符
pub static REGEX_FORMAT_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r"\{([a-zA-Z0-9_]+)\}").expect("Invalid regex pattern for REGEX_FORMAT_PATTERN"));

#[cfg(target_os = "windows")]
/// Windows环境下环境变量的正则表达式
pub static REGEX_ENV_PATTERN: Lazy<Regex> =
  Lazy::new(|| Regex::new(r"\%([a-zA-Z0-9_]+)\%").expect("Invalid regex pattern for REGEX_ENV_PATTERN on Windows"));

#[cfg(any(target_os = "linux", target_os = "macos"))]
/// Linux和macOS环境下环境变量的正则表达式
pub static REGEX_ENV_PATTERN: Lazy<Regex> =
  Lazy::new(|| Regex::new(r"\$\{([a-zA-Z0-9_]+)\}").expect("Invalid regex pattern for REGEX_ENV_PATTERN on Unix-like systems"));

#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
/// 对于其他操作系统的环境变量正则表达式
pub static REGEX_ENV_PATTERN: Lazy<Regex> =
  Lazy::new(|| Regex::new(r"\$([a-zA-Z0-9_]+)").expect("Invalid regex pattern for REGEX_ENV_PATTERN on other systems"));

impl<S: AsRef<str>> MyParseFormat for S {
  fn parse_all(&self) -> Result<String> {
    self.as_ref().parse_format()?.parse_path().parse_env()
  }

  fn parse_format(&self) -> Result<String> {
    let local: DateTime<Local> = Local::now();
    Ok(
      REGEX_FORMAT_PATTERN
        .replace_all(self.as_ref(), |caps: &regex::Captures<'_>| {
          let key: String = caps.get(1).map_or("", |m| m.as_str()).to_lowercase();
          match &*key {
            // 获取日期
            "date" => format!("{:04}-{:02}-{:02}", local.year(), local.month(), local.day()),
            // 获取日期
            "time" => format!("{:02}:{:02}:{:02}", local.hour(), local.minute(), local.second()),
            // 获取时间戳
            "timestamp-millis" => local.timestamp_millis().to_string(),
            "day" => format!("{:02}", local.day()),
            "month" => format!("{:02}", local.month()),
            "year" => format!("{:02}", local.year()),
            "hour" => format!("{:02}", local.hour()),
            "minute" => format!("{:02}", local.minute()),
            "second" => format!("{:02}", local.second()),
            "cwd" => std::env::current_dir().and_then(|x| Ok(x.display().to_string())).unwrap_or_default(),
            "nanoid" => {
              #[cfg(feature = "algorithm")]
              return crate::algorithm!(nanoid 12);
              #[cfg(not(feature = "algorithm"))]
              return String::new();
            }
            _ => String::new(),
          }
        })
        .to_string(),
    )
  }

  fn parse_env(&self) -> Result<String> {
    Ok(
      REGEX_ENV_PATTERN
        .replace_all(self.as_ref(), |caps: &regex::Captures<'_>| {
          let key = caps.get(1).map_or("", |m| m.as_str());
          let var = std::env::var(key).unwrap_or_default();
          var
        })
        .to_string(),
    )
  }

  fn parse_path(&self) -> String {
    self.as_ref().replace("\\\\", "/").replace("\\", "/")
  }

  fn parse_replace<F>(&self, start: char, end: char, match_callback: F) -> Result<String>
  where
    F: Fn(String) -> String,
  {
    let re = regex::Regex::new(&format!(r"\{}([a-zA-Z0-9_]+)\{}", start, end))?;
    Ok(
      re.replace_all(self.as_ref(), |caps: &regex::Captures<'_>| {
        let key: String = caps.get(1).map_or("", |m| m.as_str()).to_lowercase();
        match_callback(key)
      })
      .to_string(),
    )
  }
  fn parse_replace_zh<F>(&self, start: char, end: char, match_callback: F) -> Result<String>
  where
    F: Fn(String) -> String,
  {
    let re = regex::Regex::new(&format!(r"\{}([\p{{Han}}a-zA-Z0-9_@$]+)\{}", start, end))?;
    Ok(
      re.replace_all(self.as_ref(), |caps: &regex::Captures<'_>| {
        let key: String = caps.get(1).map_or("", |m| m.as_str()).to_string();
        match_callback(key)
      })
      .to_string(),
    )
  }
}

impl MyParseFormat for Path {
  fn parse_format(&self) -> Result<String> {
    self.to_string_lossy().parse_format()
  }

  fn parse_env(&self) -> Result<String> {
    self.to_string_lossy().parse_env()
  }

  fn parse_all(&self) -> Result<String> {
    self.to_string_lossy().parse_all()
  }

  fn parse_path(&self) -> String {
    self.to_string_lossy().parse_path()
  }

  fn parse_replace<F>(&self, start: char, end: char, match_value: F) -> Result<String>
  where
    F: Fn(String) -> String,
  {
    self.to_string_lossy().parse_replace(start, end, match_value)
  }
  fn parse_replace_zh<F>(&self, start: char, end: char, match_value: F) -> Result<String>
  where
    F: Fn(String) -> String,
  {
    self.to_string_lossy().parse_replace_zh(start, end, match_value)
  }
}

// 在文件末尾添加以下测试模块

#[cfg(test)]
mod tests {
  use super::*;
  use std::env;

  #[test]
  fn test_parse_all() {
    env::set_var("TEST_ENV", "test_value");
    let input = "Date: {date}, Time: {time}, Env: %TEST_ENV%";
    let result = input.parse_all().unwrap();
    assert!(result.contains("Date: "));
    assert!(result.contains("Time: "));
    assert!(result.contains("Env: test_value"));
  }

  #[test]
  fn test_parse_format() {
    let input = "{date} {time} {day} {month} {year} {hour} {minute} {second} {cwd}";
    let result = input.parse_format().unwrap();
    println!("{result}");
    assert!(!result.contains("{"));
    assert!(!result.contains("}"));
  }

  #[test]
  fn test_parse_replace() {
    let input = "Hello [name] [age]";
    let result = input
      .parse_replace('[', ']', |key| match key.as_str() {
        "name" => "Alice".to_string(),
        "age" => "30".to_string(),
        _ => key,
      })
      .unwrap();
    assert_eq!(result, "Hello Alice 30");
  }

  #[test]
  fn test_parse_env() {
    env::set_var("TEST_VAR", "test_value");
    #[cfg(target_os = "windows")]
    let input = "Path: %TEST_VAR%";
    #[cfg(any(target_os = "linux", target_os = "macos"))]
    let input = "Path: ${TEST_VAR}";
    #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
    let input = "Path: $TEST_VAR";

    let result = input.parse_env().unwrap();
    assert_eq!(result, "Path: test_value");
  }

  #[test]
  fn test_parse_path() {
    let input = r"C:\Users\Alice\Documents";
    let result = input.parse_path();
    assert_eq!(result, "C:/Users/Alice/Documents");
  }

  #[test]
  fn test_path_parse_format() {
    let path = Path::new("/home/user/{date}");
    let result = path.parse_format().unwrap();
    assert!(!result.contains("{date}"));
  }

  #[test]
  fn test_path_parse_env() {
    env::set_var("HOME", "/home/user");
    #[cfg(target_os = "windows")]
    let path = Path::new("%HOME%\\documents");
    #[cfg(any(target_os = "linux", target_os = "macos"))]
    let path = Path::new("${HOME}/documents");
    #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
    let path = Path::new("$HOME/documents");

    let result = path.parse_env().unwrap();
    assert!(result.contains("/home/user"));
  }

  #[test]
  fn test_path_parse_path() {
    let path = Path::new("C:\\Users\\Alice\\Documents");
    let result = path.parse_path();
    assert_eq!(result, "C:/Users/Alice/Documents");
  }

  #[test]
  fn test_path_parse_replace() {
    let path = Path::new("/home/[user]/[folder]");
    let result = path
      .parse_replace('[', ']', |key| match key.as_str() {
        "user" => "alice".to_string(),
        "folder" => "documents".to_string(),
        _ => key,
      })
      .unwrap();
    assert_eq!(result, "/home/alice/documents");
  }
}