//---------------------------------------------------------------------------
#include "logger.h"

#include "stdTools.h"
#include <cstring>
#include <iomanip>
#include <ctime>
#include <iostream>
#include <sstream>
#include <filesystem>
#include <sys/stat.h>

//---------------------------------------------------------------------------
Logger::Logger(std::string fileName){
	this->fileName = fileName;
	this->stop = false;
	std::string folder = Utility::BeforeLast(fileName,Utility::separator());
	if (!Utility::createFolder(folder)) {
		std::cerr << "Error create folder: " << folder << std::endl;
	}
	openLogFile();
	logThread = std::thread(&Logger::processQueue, this);
};
//---------------------------------------------------------------------------
Logger::~Logger() {
	{
		std::unique_lock<std::mutex> lock(queueMutex);
		stop = true;
	}
	queueCondition.notify_all();
	logThread.join();
};
//---------------------------------------------------------------------------
//Сервисная функция
std::string Logger::getCurrentDateTime() {
    std::time_t now = std::time(nullptr);
    std::tm* nowTm = std::localtime(&now);
    std::ostringstream timeStream;
    timeStream << std::put_time(nowTm, "%Y-%m-%dT%H:%M:%S");
    return timeStream.str();
}
//---------------------------------------------------------------------------
void Logger::log(std::string thread, std::string level, std::string data, bool cout)
{
	{
		std::unique_lock<std::mutex> lock(queueMutex);
		LogRec rec;
		rec.thread = thread;
		rec.level = level;
		rec.data = data;
		rec.cout = cout;
		logQueue.push(rec);
	}
	queueCondition.notify_one();
}
//---------------------------------------------------------------------------
void Logger::processQueue() {
	while (true) {
		{
			std::unique_lock<std::mutex> lock(queueMutex);
			queueCondition.wait(lock, [this] { return !logQueue.empty() || stop; });

			if (stop && logQueue.empty()){
				break;
			}

			//Пытаемся переименовать файл если он существует
            auto now = std::chrono::system_clock::now();
            std::time_t now_c = std::chrono::system_clock::to_time_t(now);
            std::tm* now_tm = std::localtime(&now_c);
            // Проверяем, нужно ли переименовать файл
            if (lastWriteTime != 0 && (now_c - lastWriteTime) >= 86400) {
            	if(std::filesystem::exists(fileName)){

    	    	    if(file.is_open()) {
    	    	    	file.close();
    	    	    }

    				char buffer[11]; // Длина строки "YYYY-MM-DD" + '\0'
    				std::memset(buffer, 0, sizeof(buffer));
    				std::strftime(buffer, sizeof(buffer), "%Y-%m-%d", now_tm);
    	    	    std::ostringstream newFileName;
    	    	    newFileName << Utility::BeforeLast(fileName,'.') << "_" << buffer << "." << Utility::AfterLast(fileName,'.');
    	    	    std::string newName = newFileName.str();
    	    	    if(std::filesystem::exists(newName)){ //Не знаю по какой причине если файл существует...
        	    	    std::ostringstream newFileName;
        	    	    newFileName << Utility::BeforeLast(fileName,'.') << "_" << buffer << "_" << now_c  << "." << Utility::AfterLast(fileName,'.');
        	    	    newName = newFileName.str();
    	    	    }
    				try {
    					std::filesystem::rename(fileName, newName);
    				} catch (const std::filesystem::filesystem_error& e) {
    					std::cerr << "Error renaming file: " << e.what() << std::endl;
    				}

    	        	//Удаляю старые файлы
    				std::string folder = Utility::BeforeLast(fileName,Utility::separator());
    				Utility::deleteOldFiles(folder,10);
            	}
            }

            //Записываю лог в файл
			if(!logQueue.empty()){
				LogRec rec = std::move(logQueue.front());
				logQueue.pop();
				writeLog(rec.thread,rec.level,rec.data,rec.cout);
			}

		}
	}
}
//---------------------------------------------------------------------------
void Logger::openLogFile() {
    file.open(fileName, std::ios::out | std::ios::app);
    if (!file.is_open()) {
        std::cerr << "Error open file: " << fileName << std::endl;
    }
}
//---------------------------------------------------------------------------
void Logger::writeLog(std::string thread, std::string level, std::string data, bool cout)
{
	if (!file.is_open()){
		openLogFile();
	}
	//Записываем лог
	if (file.is_open())
	{
		std::stringstream str(std::stringstream::out | std::stringstream::binary);
		std::string dateTime = getCurrentDateTime();
		thread = Utility::escape_json(thread);
		level = Utility::escape_json(level);
		data = Utility::escape_json(data);
		str << "{\"timestamp\":\"" << dateTime << "\", \"thread\":\"" <<thread<<"\", \"level\":\""<<level<<"\", \"message\":\"" << data <<"\"}" << std::endl;

		if (cout)
			std::cout << "[" << dateTime << "] " << data << std::endl; //Повтор лога в консоль

		file.write(str.str().c_str(), str.str().length());
		file.flush();

		// Обновляем время последней записи
		lastWriteTime = std::time(nullptr);
	}
}
//---------------------------------------------------------------------------
//std::string Logger::getCurrentDate() {
//    std::time_t now = std::time(nullptr);
//    std::tm* nowTm = std::localtime(&now);
//    std::ostringstream timeStream;
//    timeStream << std::put_time(nowTm, "%Y-%m-%d");
//    return timeStream.str();
//}
//---------------------------------------------------------------------------
