#pragma once

#include <Arduino.h>
#include <Wire.h>

namespace tbeam
{

  struct DateTime
  {
    uint16_t year = 0;
    uint8_t month = 0;
    uint8_t day = 0;
    uint8_t hour = 0;
    uint8_t minute = 0;
    uint8_t second = 0;
    uint8_t weekday = 0;
  };

  struct ClockConfig
  {
    uint8_t rtcAddress = 0x51;
    int sda = -1;
    int scl = -1;
    bool beginWire = true;
  };

  class TBeamClock
  {
  public:
    explicit TBeamClock(TwoWire &wire = Wire1);

    bool begin(const ClockConfig &config = ClockConfig{});
    void update();

    bool readRtc(DateTime &out, bool &lowVoltageFlag) const;
    bool readValidRtc(DateTime &out, int64_t *epochOut = nullptr) const;
    bool writeRtc(const DateTime &dt) const;

    bool ready() const { return ready_; }
    bool valid() const { return valid_; }
    bool lowVoltage() const { return lowVoltage_; }
    const DateTime &lastRtc() const { return lastRtc_; }
    int64_t lastEpoch() const { return lastEpoch_; }
    const char *lastError() const { return lastError_; }

    static bool isValidDateTime(const DateTime &dt);
    static int64_t toEpochSeconds(const DateTime &dt);
    static bool fromEpochSeconds(int64_t seconds, DateTime &out);
    static void formatIsoUtc(const DateTime &dt, char *out, size_t outSize);
    static void formatCompactUtc(const DateTime &dt, char *out, size_t outSize);
    static void makeRunId(const DateTime &dt, const char *boardId, char *out, size_t outSize);
    static bool parseDateTime(const char *text, DateTime &out);

  private:
    static uint8_t toBcd(uint8_t value);
    static uint8_t fromBcd(uint8_t value);
    static bool isLeapYear(uint16_t year);
    static uint8_t daysInMonth(uint16_t year, uint8_t month);
    static int64_t daysFromCivil(int year, unsigned month, unsigned day);

    void setError(const char *message) const;
    void clearError() const;

    TwoWire &wire_;
    ClockConfig config_{};
    bool ready_ = false;
    bool valid_ = false;
    bool lowVoltage_ = false;
    DateTime lastRtc_{};
    int64_t lastEpoch_ = 0;
    mutable char lastError_[128] = {};
  };

} // namespace tbeam
