//local
#include "logger_impl.h"
//stl
#include <fstream>
#include <random>
#include <fmt/format.h>
//boost
#include <boost/algorithm/string.hpp>
namespace robotics::v3 {
	logger_impl::~logger_impl() {
		thread_pool_.join();
	}
	void logger_impl::write(int color, std::string const& type, std::string const& time, std::string const& file, std::string const& func, int line, std::thread::id thread_id, std::string const& text) {
		if (!thread_pool_.executor().running_in_this_thread()) {
			asio::post(thread_pool_, std::bind(&logger_impl::write, this, color, type, time, file, func, line, thread_id, text));
			return;
		}


		static time_t g_remove_ergodic = 0;
		union __thread_id__ {
			__thread_id__(std::thread::id d) :
				id(d) {
			}
			std::thread::id id;
			unsigned int iid;
		};
		__thread_id__ th_id = thread_id;
		if (save_.find(type) == save_.end()) {
			std::cout << fmt::format("\033[{}m{}	[{}]	[{}#{}#{}#{}]	==>>\033[0m{}", color, time, type, file, func, line, th_id.iid, text) << std::endl;
		}
		else {
			if (!std::filesystem::is_directory("log")) {
				std::filesystem::create_directory("log");
			}
			std::string log_text = fmt::format("{}	[{}]	[{}#{}#{}#{}]	==>>{}", time, type, file, func, line, th_id.iid, text);
			std::string log_filename = fmt::format("log/{}_{}.log", type, datetime::current_datetime().to_string("yyyy-MM-dd"));
			std::fstream fs(log_filename, std::ios::out | std::ios::app);
			if (fs.is_open()) {
				fs << log_text;
				fs << std::endl;
				fs.close();
				backup(log_filename);
				time_t current_time = datetime::current_time_stamp();
				if (g_remove_ergodic == 0 || g_remove_ergodic + 43200000 < current_time) {
					g_remove_ergodic = current_time;
					remove();
				}
			}
		}
	}
	logger_impl& logger_impl::instance() {
		static logger_impl g_instance;
		return g_instance;
	}
	void logger_impl::install(std::string const& filename) {
		instance().max_time_ = config::read<int>("LOGGER", "TIME", 30);
		instance().max_size_ = config::read<int>("LOGGER", "SIZE", 10);
		auto type = config::read<std::string>("LOGGER", "TYPE", "info,debug,warn,error");
		std::vector<std::string> types;
		boost::split(types, type, boost::is_any_of(","));
		for (auto& it : types) {
			if (it.empty())
				continue;
			instance().save_.insert(it);
		}
	}
	logger_impl::logger_impl() :
		thread_pool_(1) {
	}
	void logger_impl::backup(std::string const& filename) {
		if (!std::filesystem::exists(filename) || std::filesystem::file_size(filename) <= max_size_ * 1024 * 1024)
			return;
		for (int i = 0; i < 10; i++) {
			std::string backup_filename = filename;
			std::random_device r;
			std::default_random_engine e1(r());
			std::uniform_int_distribution<int> uniform_dist(10000, 99999);
			std::string mean = "__" + std::to_string(uniform_dist(e1));
			backup_filename.insert(backup_filename.begin() + backup_filename.size() - 4, mean.begin(), mean.end());
			if (std::filesystem::exists(backup_filename))
				continue;
			std::filesystem::copy(filename, backup_filename);
			std::fstream fout(filename, std::ios::out | std::ios::trunc);
			fout.close();
			break;
		}
	}
	void logger_impl::remove() {
		datetime remove_time = datetime::current_datetime();
		remove_time.add_day(-this->max_time_);
		const std::filesystem::path path_to_directory = "log";
		try {
			if (std::filesystem::exists(path_to_directory) && std::filesystem::is_directory(path_to_directory)) {
				for (const auto& entry : std::filesystem::recursive_directory_iterator(path_to_directory)) {
					const auto& path = entry.path();
					if (std::filesystem::is_regular_file(path)) {
						auto create_time = std::filesystem::last_write_time(path);
						time_t elapse = std::chrono::duration_cast<std::chrono::milliseconds>(std::filesystem::file_time_type::clock::now().time_since_epoch() - std::chrono::system_clock::now().time_since_epoch()).count();
						std::chrono::milliseconds secs = std::chrono::duration_cast<std::chrono::milliseconds>(create_time.time_since_epoch());
						v3::datetime time = (secs.count() - elapse);
						if (remove_time > time) {
							std::filesystem::remove(path);
						}
					}
				}
			}
		}
		catch (...) {}
	}

	logger_stream::logger_stream(
		int color,
		std::string const& type,
		std::string const& time,
		std::string const& file,
		std::string const& func,
		int line,
		std::thread::id thread_id) :
		color_(color),
		type_(type),
		time_(time),
		file_(file),
		func_(func),
		line_(line),
		thread_id_(thread_id) {
	}
	logger_stream::~logger_stream() {
		std::string text = stream.str();
		if (!text.empty())
			logger_impl::instance().write(color_, type_, time_, file_, func_, line_, thread_id_, text);
	}
}