use crate::arg::get_arg;
use std::time::Duration;
// use flexi_logger::writers::FileLogWriter;
use crate::serdejson::JsonVExentd;
use crate::string::StringExentd;
use fast_log::appender::{FastLogRecord, LogAppender, RecordFormat};
use fast_log::consts::LogSize;
use fast_log::plugin::console::ConsoleAppender;
use fast_log::plugin::file_split::FileSplitAppender;
use log::LevelFilter;

// use flexi_logger::writers::LogWriter;
// use flexi_logger::{DeferredNow, FormatFunction, LevelFilter, LogSpecification, Record};
// use log::{LevelFilter, Record};

#[test]
fn test_log() {
    let _watch = crate::toml_read::run_watch("./config/default.toml").unwrap();
    crate::mylog::init_log("log-test".to_string());
    info!("this is log11111111111");
    debug!(r##"this is log22222222222222"##);
    debug!(r##"this is log3333333333333333"##);
    debug!(r##"this is log44444444444444"##);
    info!(r##"this is log55555555555555555"##);
    // error!("测试新的日志框架");
    crate::time::sleep_rand(12000, 13001);
}

static LOG_INIT: once_cell::sync::OnceCell<String> = once_cell::sync::OnceCell::new();

/// 静态日志,一个主线程只需要开启一个
pub fn init_log(log_name: impl Into<String>) {
    let log_name = log_name.into();
    let _ = LOG_INIT.get_or_init(|| {
        init_log2(log_name.clone());
        log_name
    });
}

/*pub struct MailLogWriter {}
impl flexi_logger::writers::LogWriter for MailLogWriter {
    fn write(&self, now: &mut DeferredNow, record: &Record) -> std::io::Result<()> {
        let mut msg = format!(
            "[{}] {} {}:{} {}",
            now.now().format("%Y-%m-%d %H:%M:%S%.6f"),
            record.level(),
            record.file().unwrap_or("<unnamed>"),
            record.line().unwrap_or(0),
            &record.args(),
        );
        println!("MailLogWriter {}", &msg);
        // 发送邮件
        if let Err(e) = crate::mail::send(&msg) {
            msg.push_str(&format!("\n发送邮件失败:{:?}", e));
        }
        Ok(())
    }

    fn flush(&self) -> std::io::Result<()> {
        Ok(())
    }

    fn max_log_level(&self) -> LevelFilter {
        LevelFilter::Trace
    }
}*/

/*pub struct LogWriterM {}

impl LogWriter for LogWriterM {
    fn write(&self, now: &mut DeferredNow, record: &Record) -> std::io::Result<()> {
        #[allow(unused_mut)]
        let mut msg = format!(
            "[{}] {} {}:{} {}",
            now.now().format("%Y-%m-%d %H:%M:%S%.6f"),
            record.level(),
            record.file().unwrap_or("<unnamed>"),
            record.line().unwrap_or(0),
            &record.args(),
        );

        // Todo 发送钉钉消息
        /*
        if record.level() as i64 == 1 {
            let b = get_arg("log.send_dingding_msg").unwrap_or_default() == "true";
            let url = get_arg("log.dingding_url").unwrap_or_default();
            if b && url.as_str() != "" {
                crate::send_dingding_msg::error(&msg, url);
            }
        }
        */

        // 发送邮件
        #[cfg(feature = "use_mail")]
        if record.level() == log::Level::Error {
            if let Err(e) = crate::mail_chan::send(&msg) {
                msg.push_str(&format!("\n发送邮件失败:{:?}", e));
            }
        }

        println!("{}", &msg);
        Ok(())
    }

    /*fn format(&mut self, format: FormatFunction) {
        let _ = format;

        #[allow(unused_mut)]
        let mut msg = format!(
            "[{}] {} {}:{} {}",
            now.now().format("%Y-%m-%d %H:%M:%S%.6f"),
            record.level(),
            record.file().unwrap_or("<unnamed>"),
            record.line().unwrap_or(0),
            &record.args(),
        );


        write!(w, "{}", msg)

        format()
    }*/

    fn flush(&self) -> std::io::Result<()> {
        Ok(())
    }

    fn max_log_level(&self) -> LevelFilter {
        LevelFilter::Error
    }
}*/

fn init_log2(file_name: String) {
    pub struct MailAppender {}
    impl LogAppender for MailAppender {
        fn do_logs(&self, records: &[FastLogRecord]) {
            if log::Level::Error == records[0].level {
                #[cfg(feature = "use_mail")]
                if let Err(e) = crate::mail::send(&records[0].formated) {
                    eprintln!("MailAppender Error: {:?}", e);
                }
            }
        }
    }

    let file_name = file_name.as_str();

    let appenders_config = {
        // get_arg("log.appenders").unwrap_or("console".to_owned())
        let appenders = get_arg("log.appenders");
        let r = if let Some(s) = appenders.as_str() {
            s.replace(';', ",")
        } else if let Some(arr) = appenders.as_array() {
            let mut s = String::new();
            for x in arr {
                s.push_str(",");
                s.push_str(x.as_str().unwrap_or_default());
            }
            s
        } else {
            appenders.to_string()
        };
        let r = r.trim().trim_start_matches(",").trim_end_matches(",").trim().to_string();
        if r.trim() == "" {
            "console".to_owned()
        } else {
            r
        }
    };

    let level_str = get_arg("log.level").as_string2().unwrap_or("info".to_owned());
    let level = match level_str.to_lowercase().trim() {
        "trace" => LevelFilter::Trace,
        "debug" => LevelFilter::Debug,
        "info" => LevelFilter::Info,
        "warn" => LevelFilter::Warn,
        "error" => LevelFilter::Error,
        _ => LevelFilter::Info,
    };

    let mut config = fast_log::config::Config::new().level(level);

    let mut filters = vec![];
    let filters_input = get_arg("log.filters");
    if let Some(filters_input) = filters_input.as_array() {
        for x in filters_input {
            filters.push(x.as_str().unwrap_or_default().replace('-', "_"));
        }
    }
    if !filters.is_empty() {
        config = config.filter(fast_log::filter::ModuleFilter::new_include(filters));
    }

    std::env::set_var("RUST_LOG", level_str);

    let storage_size = get_arg("log.storage_size").as_u64().unwrap_or_else(|| 25);

    if appenders_config.contains("file") {
        config = config.custom(FileSplitAppender::new(
            &format!("logs-{}/", file_name),
            LogSize::MB(storage_size as usize),
            fast_log::plugin::file_split::RollingType::KeepTime(Duration::from_secs(24_u64 * 3600)),
            Box::new(fast_log::plugin::packer::LogPacker {}),
        ));
    }

    if appenders_config.contains("mail") {
        config = config.custom(MailAppender {});
    }

    if appenders_config.contains("console") {
        config = config.custom(ConsoleAppender {});
    }

    pub struct MyFormat {}
    impl RecordFormat for MyFormat {
        fn do_format(&self, arg: &mut FastLogRecord) {
            let line = arg.line.map(|v| v.to_string()).unwrap_or_default();
            let now = chrono::DateTime::<chrono::Local>::from(arg.now);
            let now = format!("{:26}", now.to_string()).sub_string(0, 26);

            arg.formated = format!(
                "[{now}] {level} [{module_path}:{line}] {formated} {args}\n",
                now = now,
                module_path = arg.file,
                line = line,
                level = arg.level,
                formated = arg.formated.trim_start_matches("(").trim_end_matches(")"),
                args = arg.args,
            );
        }
    }
    config = config.format(MyFormat {});

    fast_log::init(config).unwrap();
}

/*/// 初始化日志
/// #[macro_use]
/// extern crate log;
/// extern crate chrono;
/// extern crate env_logger;
/// code: info!("{}", "info msg");*/
/*fn init_log2(file_name: String) {
    use flexi_logger::{Age, Cleanup, Criterion, Naming};
    let file_name = file_name.as_str();

    let get_log = |log: String| match log.trim() {
        "trace" | "debug" | "info" | "warn" | "error" => log,
        _ => "info".to_owned(),
    };

    let other_log = crate::arg::get_os_arg("log.other.mode").unwrap_or(String::from("info"));
    let other_log = get_log(other_log);

    let default_level = get_arg("log.default").unwrap_or("warn".to_owned());
    let default_level = &get_log(default_level);

    std::env::set_var("RUST_LOG", default_level);
    // std::env::set_var("actix_server", default_level);
    // std::env::set_var("actix_web", default_level);

    let filters = {
        let mut filters = String::new();
        for i in 0.. {
            match get_arg(format!("log.other.filters[{}]", i)) {
                Some(v) => filters.push_str(format!("{}={},", v, other_log).as_str()),
                None => break,
            }
        }
        filters.trim_end_matches(",").to_string()
    };

    let log = &format!("{},{}", default_level, filters);
    let msg = format!("log file write to: logs/{}, log mode: {} ", file_name, log);
    println!("{}", msg);
    let mut logger = flexi_logger::Logger::with(LogSpecification::parse(log).unwrap_or_default());
    // let mut logger = flexi_logger::Logger::with_env_or_str(log);
    let not_log_to_file = get_arg("log.not_write_file").unwrap_or_default() == "true";
    if !not_log_to_file {
        logger = logger.log_to_file();
    }

    // let fw = FileLogWriter::builder()
    //     .basename(file_name)
    //     .directory("logs")
    //     .rotate(
    //         Criterion::Age(Age::Day),
    //         Naming::Timestamps,
    //         Cleanup::KeepLogFiles(30),
    //     )
    //     .try_build()
    //     .unwrap();

    let log_r = logger
        .duplicate_to_stdout(flexi_logger::Duplicate::All)
        .append()
        .add_writer("mylog", Box::new(LogWriterM {}))
        .append()
        .use_windows_line_ending()
        .basename(file_name)
        .directory("logs")
        .format(|w, now, record| {

            #[allow(unused_mut)]
            let mut msg = format!(
                "logger[{}] {} {}:{} {}",
                now.now().format("%Y-%m-%d %H:%M:%S%.6f"),
                record.level(),
                record.file().unwrap_or("<unnamed>"),
                record.line().unwrap_or(0),
                &record.args(),
            );
            write!(w, "{}", msg)
        })
        .print_message()
        .rotate(
            Criterion::Age(Age::Day),
            Naming::Timestamps,
            Cleanup::KeepLogFiles(30),
        )
        // .add_writer("mail_log", Box::new(MailLogWriter {}))
        .start()
        .map(|_v| "runing is OK!");

    //set_log(tx);

    warn!("{}, {:?}", msg, log_r);
    // env_logger::init();
}*/
