#include "daily_and_size_sink.h"
#include <charconv>
#include <regex>

#include "date_util.h"

namespace zsy
{
    DailyAndSizeSink::DailyAndSizeSink(const std::string &filename, uintmax_t maxSize, int32_t maxDays)
        : maxSize(maxSize)
          , maxDays(maxDays)
    {
        std::filesystem::path base_path(filename);
        auto paths = base_path.parent_path();
        basePath = paths.string();
        baseFilename = base_path.stem().string();
        baseExt = base_path.extension().string();
        std::filesystem::create_directories(paths);
        currentDate = DateUtil::format(Ymd_);
        currentIndex = lastIndex();
        currentSize = lastSize();
        fileHelper.open(basePath + "\\" + baseFilename + baseExt, false);
    }

    void DailyAndSizeSink::rotateFile()
    {
        auto current_date = DateUtil::format(Ymd_);

        if (current_date != currentDate)
        {
            fileHelper.flush();
            fileHelper.close();
            std::filesystem::rename(basePath + "\\" + baseFilename + baseExt, basePath + "\\" + baseFilename + "." + currentDate + "." + std::to_string(currentIndex) + baseExt);
            fileHelper.open(basePath + "\\" + baseFilename + baseExt, false);
            currentDate = current_date;
            currentIndex = 1;
        } else
        {
            if (currentSize >= maxSize)
            {
                fileHelper.flush();
                fileHelper.close();
                std::filesystem::rename(basePath + "\\" + baseFilename + baseExt, basePath + "\\" + baseFilename + "." + currentDate + "." + std::to_string(currentIndex) + baseExt);
                fileHelper.open(basePath + "\\" + baseFilename + baseExt, false);
                currentIndex++;
            }
        }
    }

    void DailyAndSizeSink::cleanOldFiles()
    {
        try
        {
            if (maxDays <= 0) return;

            auto now = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
            auto cutoff_time = now - std::chrono::days(maxDays);

            // 正则表达式模式：baseName.YYYY-MM-DD(.index)?.ext
            std::regex file_pattern(baseFilename + R"(\.(\d{4}-\d{2}-\d{2})(\.\d+)?)" + baseExt);

            std::filesystem::path paths(basePath);
            for (const auto &entry: std::filesystem::directory_iterator(paths))
            {
                if (entry.is_regular_file())
                {
                    std::string filename = entry.path().filename().string();
                    std::smatch match;

                    if (std::regex_match(filename, match, file_pattern))
                    {
                        std::string date_str = match[1].str();
                        std::chrono::sys_days date;
                        std::istringstream tstr(date_str);
                        tstr >> std::chrono::parse("%F", date);
                        if (date < cutoff_time)
                        {
                            std::filesystem::remove(entry.path());
                        }
                    }
                }
            }
        } catch (const std::exception &e)
        {
            // 清理失败
        }
    }

    void DailyAndSizeSink::sink_it_(const spdlog::details::log_msg &msg)
    {
        spdlog::memory_buf_t formatted;
        formatter_->format(msg, formatted);
        std::string formatted_str(formatted.data(), formatted.size());
        rotateFile();
        fileHelper.write(formatted);
        currentSize = fileHelper.size();
        cleanOldFiles();
    }

    void DailyAndSizeSink::flush_()
    {
        fileHelper.flush();
    }

    uint32_t DailyAndSizeSink::lastIndex()
    {
        uint32_t index = 0;
        std::regex file_pattern(baseFilename + R"(\.\d{4}-\d{2}-\d{2}\.(\d+))" + baseExt);
        std::filesystem::path paths(basePath);
        for (const auto &entry: std::filesystem::directory_iterator(paths))
        {
            if (entry.is_regular_file())
            {
                std::string filename = entry.path().filename().string();
                std::smatch match;

                if (std::regex_match(filename, match, file_pattern))
                {
                    std::string index_str = match[1].str();
                    auto index_chp = index_str.data();
                    int index_{};
                    std::from_chars(index_chp, index_chp + index_str.size(), index_);
                    if (index_ > index) index = index_;
                }
            }
        }
        return index + 1;
    }

    uintmax_t DailyAndSizeSink::lastSize()
    {
        uintmax_t size = 0;
        std::filesystem::path filePath(basePath + "\\" + baseFilename + baseExt);
        if (std::filesystem::exists(filePath))
        {
            size = std::filesystem::file_size(filePath);
        }
        return size;
    }
}
