#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;
}

// ---------------------------------------------------------------------------
// Battery full calibration
//
// The voltage estimate above tops out around 98% on real hardware even when
// the PMIC reports the pack is fully charged: 4.1x V under charge never reaches
// the 4.20 V "100%" knee, and the 7:1 filter plus ADC noise keep it a few mV
// short. Users therefore never see 100%.
//
// We keep the raw estimate untouched and derive a separate *display* percent.
// Calibration is conservative and evidence-based, never "98 => 100":
//
//   FULL requires BOTH
//     - the PMIC reports charge complete (CHG_STAT deasserted while external
//       power is present, i.e. the charger has stopped pushing current), and
//     - the filtered pack voltage is at/above FULL_VOLTAGE_MV.
//
//   Once FULL, the display only drops back below 100% when the voltage falls
//   below FULL_RELEASE_MV (hysteresis), so plug/unplug does not oscillate
//   98 <-> 100. Unplugging removes external power, which immediately drops the
//   restore condition, but the voltage re-check still guards the transition.
//
// `charge_complete` is the raw PMIC signal; see BatteryStatus::sample.
constexpr uint16_t BATTERY_FULL_VOLTAGE_MV = 4150;
constexpr uint16_t BATTERY_FULL_RELEASE_MV = 4090;

// Display percent for a valid sample. `full_latched` carries calibration state
// across samples so the 100% decision can use hysteresis. It is updated in
// place. When no positive full evidence exists this returns the raw estimate
// unchanged, so ordinary 98% still reads 98%.
inline uint8_t battery_display_percent(uint16_t filtered_mv, bool charge_complete,
                                       bool external_power, bool &full_latched) {
    const uint8_t raw = battery_percent(filtered_mv);
    if (full_latched) {
        // Stay at 100% until voltage clearly falls or the charger resumes.
        if (!external_power || filtered_mv < BATTERY_FULL_RELEASE_MV || !charge_complete)
            full_latched = false;
        else
            return 100;
    }
    if (external_power && charge_complete && filtered_mv >= BATTERY_FULL_VOLTAGE_MV) {
        full_latched = true;
        return 100;
    }
    return raw;
}

// Charging visual state for the Watch battery row.
enum class ChargeState : uint8_t {
    Discharging,  // On battery, no external power.
    Charging,     // External power present and the charger is pushing current.
    Full,         // External power present and charge complete.
    ExternalIdle, // External power present, charger neither charging nor done.
};

inline ChargeState charge_state(bool external_power, bool charging, bool charge_complete) {
    if (!external_power) return ChargeState::Discharging;
    if (charge_complete) return ChargeState::Full;
    if (charging) return ChargeState::Charging;
    return ChargeState::ExternalIdle;
}

struct BatteryStatus {
    bool valid=false,external_power=false,charging=false;
    // PMIC reports charge complete (CHG_STAT deasserted with external power).
    // Distinct from `charging`: complete means the charger has stopped.
    bool charge_complete=false;
    uint16_t raw_millivolts=0,filtered_millivolts=0;
    // Raw voltage estimate, kept truthful for diagnostics.
    uint8_t percent=0;
    // Calibrated value the UI shows; only differs from `percent` when full
    // calibration latches 100%.
    uint8_t display_percent=0;
    ChargeState charge=ChargeState::Discharging;
    uint32_t failures=0;
    int last_error=0;
    static constexpr int InvalidVoltage=1; // Distinct from vendor's negative errors.
    bool _full_latched=false;

    // Recompute the display value from the currently held flags/voltage.
    void recalibrate() {
        display_percent=battery_display_percent(filtered_millivolts,charge_complete,
                                                external_power,_full_latched);
        charge=charge_state(external_power,charging,charge_complete);
    }
    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;
        recalibrate();
    }
};
} // namespace wearable
