BareGit
#include "backup.hpp"

#include <chrono>
#include <filesystem>
#include <format>
#include <string>

#include <mw/database.hpp>
#include <mw/error.hpp>
#include <mw/utils.hpp>
#include <spdlog/spdlog.h>

namespace overseer::db
{

namespace
{

std::string nowStamp()
{
    auto now = std::chrono::system_clock::now();
    return std::format("{:%Y%m%d-%H%M%S}",
                       std::chrono::floor<std::chrono::seconds>(now));
}

// Single-quote escape for inline SQL string literals.
std::string sqlQuote(const std::string& s)
{
    std::string out;
    out.reserve(s.size() + 2);
    out.push_back('\'');
    for(char c : s)
    {
        if(c == '\'')
        {
            out.push_back('\'');
            out.push_back('\'');
        }
        else
        {
            out.push_back(c);
        }
    }
    out.push_back('\'');
    return out;
}

} // namespace

mw::E<std::filesystem::path>
backupDatabaseFile(mw::SQLite& db, const std::string& db_path)
{
    std::filesystem::path src = db_path;
    auto abs = std::filesystem::absolute(src);
    auto dest = abs;
    dest += ".bak-" + nowStamp();

    // VACUUM INTO is the documented online-backup-equivalent SQL form
    // and it is safe with WAL active. See
    // https://www.sqlite.org/lang_vacuum.html#vacuuminto
    std::string sql = "VACUUM INTO " + sqlQuote(dest.string()) + ";";
    if(auto rt = db.execute(sql.c_str()); !rt.has_value())
    {
        return std::unexpected(mw::runtimeError(std::format(
            "Failed to back up database: {}", mw::errorMsg(rt.error()))));
    }
    spdlog::info("Database backed up to {}", dest.string());
    return dest;
}

} // namespace overseer::db