use std::env;

use colored::Colorize;
use fern::Dispatch;
use log::{Record, SetLoggerError};

fn format_regular<S: Into<String>>(log: S, record: &Record) -> String {
    let log = log.into();
    let line_prefix = |line: String, extend: bool| {
        let prefix = if extend {
            match record.level() {
                log::Level::Trace => "      ]".bright_blue(),
                log::Level::Debug => " ?".green(),
                log::Level::Info => " >".blue(),
                log::Level::Warn => " #".yellow(),
                log::Level::Error => " !".red(),
            }.to_string()
        } else {
            match record.level() {
                log::Level::Trace => "[TRACE]".bright_blue(),
                log::Level::Debug => "??".green(),
                log::Level::Info => "=>".blue(),
                log::Level::Warn => "##".yellow(),
                log::Level::Error => "!!".red()
            }.to_string()
        };
        return format!("{} {}", prefix, line);
    };

    let mut lines = log.lines().peekable();
    let mut output = match lines.peek() {
        Some(_line) => line_prefix(lines.next().unwrap().to_string(), false),
        None => return "".to_string(),
    };

    for line in lines {
        output.push_str(&*format!("\n{}", line_prefix(line.to_string(), true)));
    }

    output
}

pub fn setup_logger() -> Result<(), SetLoggerError> {
    Dispatch::new()
        .format(|out, message, record| {
            match record.metadata().target() {
                // command output logging
                "command:stdout" => out.finish(format_args!("{} {}", ">>".cyan(), message.to_string())),
                "command:stderr" => out.finish(format_args!("{} {}", ">>".red(), message.to_string())),
                // this target means, it's an item and not a log.
                "item" => out.finish(format_args!("{} {}", "*".blue(), message.to_string())),
                // default logging
                _ => out.finish(format_args!("{}", format_regular(message.to_string(), record))),
            }
        })
        .level(
            env::var("PKGR_LOG_LEVEL")
                .unwrap_or_else(|_| "info".to_string())
                .parse()
                .unwrap_or(log::LevelFilter::Info),
        )
        .chain(std::io::stdout())
        .apply()
}