/* -*- mode: c++ -*-
 * Copyright 2025 Dracal Technologies Inc. All rights reserved.
 */

#include <dracal/common/chrono.hpp>
#include <dracal/common/fmt.hpp>
#include <dracal/common/platform.hpp>

#include <ctime>

namespace {
std::tm to_local_time(const chrono::time_point<chrono::system_clock> tp) {
    const auto time_t_val = chrono::system_clock::to_time_t(tp);
    std::tm local_tm{};
#if defined(DRACAL_WINDOWS)
    localtime_s(&local_tm, &time_t_val);
#else
    localtime_r(&time_t_val, &local_tm);
#endif
    return local_tm;
}
} // anonymous namespace

namespace dracal::common {

std::string format_timestamp_local_YYYYMMDDTHHMMSS(const chrono::time_point<chrono::system_clock> tp) {
    const auto local_tm = to_local_time(tp);
    return fmt::format("{:%Y-%m-%dT%H:%M:%S}", local_tm);
}

std::string format_timestamp_local_YYYYMMDDTHHMMSSmmm(const chrono::time_point<chrono::system_clock> tp) {
    const auto local_tm = to_local_time(tp);
    const auto millis = chrono::duration_cast<chrono::milliseconds>(tp.time_since_epoch()) % 1000;
    return fmt::format("{:%Y-%m-%dT%H:%M:%S}.{:03}", local_tm, millis.count());
}

std::array<std::string, 2> format_timestamp_local_YYYYMMDD_HHMMSS(const chrono::time_point<chrono::system_clock> tp) {
    const auto local_tm = to_local_time(tp);
    return {fmt::format("{:%Y-%m-%d}", local_tm), fmt::format("{:%H:%M:%S}", local_tm)};
}

std::array<std::string, 2>
format_timestamp_local_YYYYMMDD_HHMMSSmmm(const chrono::time_point<chrono::system_clock> tp) {
    const auto local_tm = to_local_time(tp);
    const auto millis = chrono::duration_cast<chrono::milliseconds>(tp.time_since_epoch()) % 1000;
    return {fmt::format("{:%Y-%m-%d}", local_tm), fmt::format("{:%H:%M:%S}.{:03}", local_tm, millis.count())};
}

std::string format_timestamp_local_HHMMSS(const chrono::time_point<chrono::system_clock> tp) {
    const auto local_tm = to_local_time(tp);
    return fmt::format("{:%H:%M:%S}", local_tm);
}

std::string format_timestamp_local_HHMMSSmmm(const chrono::time_point<chrono::system_clock> tp) {
    const auto local_tm = to_local_time(tp);
    const auto millis = chrono::duration_cast<chrono::milliseconds>(tp.time_since_epoch()) % 1000;
    return fmt::format("{:%H:%M:%S}.{:03}", local_tm, millis.count());
}

std::string format_timestamp_milliseconds_from_epoch(const chrono::time_point<chrono::system_clock> tp) {
    const auto millis = chrono::duration_cast<chrono::milliseconds>(tp.time_since_epoch());
    return fmt::format("{}", millis.count());
}

std::string format_timestamp_local_system_default(const chrono::time_point<chrono::system_clock> tp) {
    const auto local_tm = to_local_time(tp);
    return fmt::format("{:%c}", local_tm);
}

std::string utc_timestamp_millis(const chrono::time_point<chrono::system_clock> tp) {
    return fmt::format("{:%Y-%m-%dT%H:%M:%S}.{:03d}Z", chrono::floor<std::chrono::seconds>(tp),
                       chrono::duration_cast<chrono::milliseconds>(tp.time_since_epoch()).count() % 1000);
}

} // namespace dracal::common
