#pragma once
#include <cstdint>
#include <cmath>

namespace wearable {
constexpr int DEMO_TIMEZONE_OFFSET_MINUTES = 480;
struct WallClock {
    uint64_t utc_base_ms=0, monotonic_base_ms=0;
    int16_t timezone_offset_minutes=0;
    bool sync(double epoch_ms,uint64_t now_ms,int offset_minutes=0) {
        // 2024..2100; accept only integer milliseconds from authenticated hello.
        if(!std::isfinite(epoch_ms) || epoch_ms<1704067200000.0 || epoch_ms>=4102444800000.0 ||
            std::floor(epoch_ms)!=epoch_ms || offset_minutes < -720 || offset_minutes > 840) return false;
        utc_base_ms=uint64_t(epoch_ms);monotonic_base_ms=now_ms;
        timezone_offset_minutes=offset_minutes;return true;
    }
    uint64_t utc_ms(uint64_t now_ms) const {
        return utc_base_ms && now_ms>=monotonic_base_ms?utc_base_ms+now_ms-monotonic_base_ms:0;
    }
    int64_t local_ms(uint64_t now_ms) const {
        const auto utc=utc_ms(now_ms);
        // Demo display is UTC+8 even when Gateway/NVS retain offset=0.
        // UTC epoch, monotonic base and RTC writes remain untouched.
        return utc?int64_t(utc)+int64_t(DEMO_TIMEZONE_OFFSET_MINUTES)*60000:0;
    }
};
inline bool battery_voltage_valid(uint16_t mv) {return mv>=2000 && mv<=5000;}
inline uint8_t battery_percent(uint16_t mv) {
    return mv<=3300?0:mv>=4200?100:uint32_t(mv-3300)*100/900;
}
inline uint16_t battery_filter(uint16_t old_mv,uint16_t new_mv) {
    // Official StopWatch 7:1 filter, rounded to the nearest mV.
    return old_mv?uint16_t((uint32_t(old_mv)*7+new_mv+4)/8):new_mv;
}
struct BatteryStatus {
    bool valid=false,external_power=false,charging=false;
    uint16_t raw_millivolts=0,filtered_millivolts=0;
    uint8_t percent=0;
    uint32_t failures=0;
    int last_error=0;
    static constexpr int InvalidVoltage=1; // Distinct from vendor's negative errors.
    void sample(int result,uint16_t mv) {
        raw_millivolts=mv;
        last_error=result?result:battery_voltage_valid(mv)?0:InvalidVoltage;
        if(last_error) {++failures;return;} // Keep the last valid sample on failure.
        filtered_millivolts=battery_filter(valid?filtered_millivolts:0,mv);
        percent=battery_percent(filtered_millivolts);valid=true;
    }
};
} // namespace wearable
