#pragma once
#include <cstddef>
#include <cstdint>

namespace wearable {
struct RtcFields {
    uint16_t year=0;
    uint8_t month=0,day=0,weekday=0,hour=0,minute=0,second=0;
};

inline bool leap_year(unsigned year) {
    return year%4==0 && (year%100!=0 || year%400==0);
}
inline uint8_t month_days(unsigned year,unsigned month) {
    static constexpr uint8_t days[]={31,28,31,30,31,30,31,31,30,31,30,31};
    return month>=1 && month<=12?uint8_t(days[month-1]+(month==2 && leap_year(year))):0;
}
inline bool valid_rtc_fields(const RtcFields& value) {
    return value.year>=2024 && value.year<=2099 && value.month>=1 && value.month<=12 &&
        value.day>=1 && value.day<=month_days(value.year,value.month) && value.weekday<=6 &&
        value.hour<=23 && value.minute<=59 && value.second<=59;
}
inline uint8_t bcd(unsigned value) {return uint8_t((value/10)<<4 | value%10);}
inline bool bcd_value(uint8_t value,unsigned maximum,uint8_t& decoded) {
    if((value&0x0f)>9 || ((value>>4)&0x0f)>9) return false;
    const unsigned result=(value>>4)*10+(value&0x0f);
    if(result>maximum) return false;
    decoded=uint8_t(result);return true;
}
inline bool rtc_fields_to_epoch_ms(const RtcFields& value,uint64_t& epoch_ms) {
    if(!valid_rtc_fields(value)) return false;
    uint64_t days=0;
    for(unsigned year=1970;year<value.year;++year) days+=leap_year(year)?366:365;
    for(unsigned month=1;month<value.month;++month) days+=month_days(value.year,month);
    days+=value.day-1;
    if((days+4)%7!=value.weekday) return false;
    epoch_ms=(((days*24+value.hour)*60+value.minute)*60+value.second)*1000;
    return true;
}
inline bool epoch_ms_to_rtc_fields(uint64_t epoch_ms,RtcFields& value) {
    uint64_t seconds=epoch_ms/1000,days=seconds/86400;
    const uint64_t day_seconds=seconds%86400;
    unsigned year=1970;
    while(year<=2099) {
        const unsigned count=leap_year(year)?366:365;
        if(days<count) break;
        days-=count;++year;
    }
    if(year<2024 || year>2099) return false;
    unsigned month=1;
    while(month<=12 && days>=month_days(year,month)) {days-=month_days(year,month);++month;}
    if(month>12) return false;
    // Recompute absolute days for the weekday; bounded loops keep this explicit.
    uint64_t absolute_days=0;
    for(unsigned y=1970;y<year;++y) absolute_days+=leap_year(y)?366:365;
    for(unsigned m=1;m<month;++m) absolute_days+=month_days(year,m);
    absolute_days+=days;
    value.year=year;value.month=month;value.day=days+1;value.weekday=(absolute_days+4)%7;
    value.hour=day_seconds/3600;value.minute=(day_seconds/60)%60;value.second=day_seconds%60;
    return true;
}
inline bool decode_rx8130(const uint8_t* registers,size_t size,RtcFields& value) {
    if(!registers || size!=7 || registers[3]==0 || (registers[3]&(registers[3]-1))) return false;
    RtcFields next;
    if(!bcd_value(registers[0]&0x7f,59,next.second) ||
       !bcd_value(registers[1]&0x7f,59,next.minute) ||
       !bcd_value(registers[2]&0x3f,23,next.hour) ||
       !bcd_value(registers[4]&0x3f,31,next.day) ||
       !bcd_value(registers[5]&0x1f,12,next.month)) return false;
    uint8_t year=0;if(!bcd_value(registers[6],99,year)) return false;
    next.year=2000+year;
    for(uint8_t bit=registers[3];(bit>>=1);++next.weekday) {}
    uint64_t ignored=0;if(!rtc_fields_to_epoch_ms(next,ignored)) return false;
    value=next;return true;
}
inline bool encode_rx8130(uint64_t epoch_ms,uint8_t* registers,size_t size) {
    if(!registers || size!=7) return false;
    RtcFields value;if(!epoch_ms_to_rtc_fields(epoch_ms,value)) return false;
    registers[0]=bcd(value.second);registers[1]=bcd(value.minute);registers[2]=bcd(value.hour);
    registers[3]=uint8_t(1U<<value.weekday);registers[4]=bcd(value.day);
    registers[5]=bcd(value.month);registers[6]=bcd(value.year%100);return true;
}
} // namespace wearable
