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

#pragma once

#include <dracal/common/chrono.hpp>

#include <spdlog/details/log_msg.h>
#include <spdlog/sinks/base_sink.h>

#include <deque>
#include <mutex>
#include <string>
#include <vector>

namespace dracal::logging {

enum class file_policy { overwrite = 0, append = 1 };

enum class timestamp_policy {
    none = 0,
    system_default = 1,
    iso_8601_long = 2,
    iso_8601_long_dual = 3,
    iso_8601_short = 4,
    iso_8601_long_ms = 5,
    iso_8601_long_ms_dual = 6,
    iso_8601_short_ms = 7,
    epoch_ms = 8,
};

namespace utils {

/**
 * @brief Creates a comment section for the log file.
 *
 * Creates a comment section for the log file with the current timestamp, interval, and comment.
 *
 * @param[in] now The timestamp to use.
 * @param[in] interval The interval to use.
 * @param[in] comment The comment to use.
 * @return Lines of the comment section, including a trailing empty line.
 */
std::vector<std::string> create_comment_section(const chrono::time_point<chrono::system_clock> now,
                                                const chrono::milliseconds interval, const std::string &comment);

/**
 * @brief Creates a header section for the log file.
 *
 * Creates a header section for the log file with the separator, timestamp column names, product names, channel names,
 * channel descriptions, and unit symbols.
 *
 * @param[in] separator The separator to use between columns.
 * @param[in] timestamp_column_names The timestamp column header name(s).
 * @param[in] product_names The product name for each channel.
 * @param[in] channel_names The channel name for each channel.
 * @param[in] channel_descriptions The channel description for each channel.
 * @param[in] unit_symbols The unit symbol for each channel.
 * @return Four lines: product names row, channel names row, descriptions row, units row.
 */
std::vector<std::string> create_header_section(const std::string &separator,
                                               const std::vector<std::string> &timestamp_column_names,
                                               const std::vector<std::string> &product_names,
                                               const std::vector<std::string> &channel_names,
                                               const std::vector<std::string> &channel_descriptions,
                                               const std::vector<std::string> &unit_symbols);

/**
 * @brief Returns the timestamp column header names for a given timestamp policy.
 *
 * @param[in] policy The timestamp policy.
 * @return Column header name(s); empty for `none`, one element for single-column policies, two for dual-column policies.
 */
std::vector<std::string> timestamp_column_names(const timestamp_policy policy);

/**
 * @brief Formats a timestamp according to the given timestamp policy.
 *
 * @param[in] policy The timestamp policy.
 * @param[in] ts The timestamp to format.
 * @return Formatted string(s); size matches the vector returned by `timestamp_column_names(policy)`.
 */
std::vector<std::string> format_timestamp(const timestamp_policy policy,
                                          const chrono::time_point<chrono::system_clock> ts);

} // namespace utils

/**
 * @brief Custom memory sink for spdlog that stores log messages in a deque.
 *
 * This sink stores log messages in memory using a std::deque with a
 * maximum size limit. When the limit is reached, oldest messages are discarded.
 */
class memory_sink_mt : public spdlog::sinks::base_sink<std::mutex> {
  public:
    /**
     * @brief Constructor for memory sink.
     * @param[in] max_lines Maximum number of lines to keep in memory (0 = no logging).
     */
    explicit memory_sink_mt(const size_t max_lines);

    /**
     * @brief Gets the maximum number of lines.
     *
     * @return Maximum number of lines configured for this sink (0 = no logging).
     */
    size_t max_lines() const { return _max_lines; }

    /**
     * @brief Gets a copy of all logged lines.
     *
     * Thread-safe access using the sink's internal mutex.
     *
     * @return Copy of the deque containing all logged lines, each including the line terminator.
     */
    std::deque<std::string> lines();

  protected:
    /**
     * @brief Sink implementation that formats and stores the log message.
     * @param[in] msg The log message to be stored.
     */
    void sink_it_(const spdlog::details::log_msg &msg) override;

    /**
     * @brief Flush implementation (no-op for memory sink).
     */
    void flush_() override;

  private:
    const size_t _max_lines;
    std::deque<std::string> _lines;
};

} // namespace dracal::logging