use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::Path;

use chrono::Local;
use colored::Colorize;
use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};

pub(crate) struct ServerLogger;
static LOGGER: ServerLogger = ServerLogger;

impl ServerLogger {
    pub(crate) fn init() -> Result<(), SetLoggerError> {
        log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
    }

    fn log_to_console(record: &Record) {
        let formatted_message = format!(
            "[{}] [{}] [{}:{}]\n{}",
            Local::now().to_rfc3339(),
            match record.level() {
                Level::Error => "ERROR".red(),
                Level::Warn => "WARN".yellow(),
                Level::Info => "INFO".green(),
                Level::Debug => "DEBUG".blue(),
                Level::Trace => "TRACE".blue(),
            },
            record.file().expect("Failed to get file name!"),
            record.line().expect("Failed to get line number!"),
            record.args()
        );

        println!("{formatted_message}")
    }

    fn log_to_file(record: &Record) {
        let formatted_message = format!(
            "[{}] [{}] [{}:{}]\n{}",
            Local::now().to_rfc3339(),
            record.level(),
            record.file().expect("Failed to get file name!"),
            record.line().expect("Failed to get line number!"),
            record.args()
        );

        let logging_dir = Path::new("./logs");

        if !logging_dir.exists() {
            fs::create_dir_all(logging_dir).expect("Failed to create directory!");
        }
        let mut logging_file = OpenOptions::new()
            .append(true)
            .create(true)
            .open(logging_dir.join("server_log.txt"))
            .expect("Failed to create logging file!");

        writeln!(logging_file, "{formatted_message}").expect("Failed to write into logging file!");
    }
}

impl Log for ServerLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Info
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            ServerLogger::log_to_console(record);
            ServerLogger::log_to_file(record);
        }
    }

    fn flush(&self) {}
}
