use crate::event::metrics::RuntimePackedCalendarTuple;
use crate::{CalendarPoint, RuntimeState};

pub fn runtime_world_absolute_counter(state: &RuntimeState) -> Option<u32> {
    state.world_restore.absolute_counter_raw_u32
}

pub fn runtime_world_partial_year_weight_numerator(state: &RuntimeState) -> Option<i64> {
    Some(i64::from(state.world_restore.partial_year_progress_raw_u8?) * 5 - 5)
}

pub fn runtime_decode_packed_calendar_tuple(
    word_0: u32,
    word_1: u32,
) -> RuntimePackedCalendarTuple {
    let bytes_0 = word_0.to_le_bytes();
    let bytes_1 = word_1.to_le_bytes();
    RuntimePackedCalendarTuple {
        year_word: u16::from_le_bytes([bytes_0[0], bytes_0[1]]),
        month_1_based: bytes_0[2],
        week_1_based: bytes_0[3],
        day_1_based: bytes_1[0],
        hour_0_based: bytes_1[1],
        quarter_day_1_based: bytes_1[2],
        minute_0_based: bytes_1[3],
    }
}

pub fn runtime_encode_packed_calendar_tuple(tuple: RuntimePackedCalendarTuple) -> (u32, u32) {
    let year_bytes = tuple.year_word.to_le_bytes();
    let word_0 = u32::from_le_bytes([
        year_bytes[0],
        year_bytes[1],
        tuple.month_1_based,
        tuple.week_1_based,
    ]);
    let word_1 = u32::from_le_bytes([
        tuple.day_1_based,
        tuple.hour_0_based,
        tuple.quarter_day_1_based,
        tuple.minute_0_based,
    ]);
    (word_0, word_1)
}

pub fn runtime_derive_packed_calendar_tuple_from_calendar_point(
    calendar: CalendarPoint,
) -> Option<RuntimePackedCalendarTuple> {
    let year_word = u16::try_from(calendar.year).ok()?;
    let month_1_based = u8::try_from(calendar.month_slot.checked_add(1)?).ok()?;
    let day_1_based = u8::try_from(calendar.phase_slot.checked_add(1)?).ok()?;
    let week_1_based = u8::try_from(calendar.phase_slot / 7 + 1).ok()?;
    let total_minutes = calendar.tick_slot.checked_mul(1440)? / crate::calendar::TICKS_PER_PHASE;
    let hour_0_based = u8::try_from(total_minutes / 60).ok()?;
    let minute_0_based = u8::try_from(total_minutes % 60).ok()?;
    let quarter_day_1_based = u8::try_from((u32::from(hour_0_based) / 6) + 1).ok()?;
    Some(RuntimePackedCalendarTuple {
        year_word,
        month_1_based,
        week_1_based,
        day_1_based,
        hour_0_based,
        quarter_day_1_based,
        minute_0_based,
    })
}

pub fn runtime_pack_packed_calendar_tuple_to_absolute_counter(
    tuple: RuntimePackedCalendarTuple,
) -> Option<u32> {
    if !(1..=12).contains(&tuple.month_1_based) {
        return None;
    }
    if !(1..=28).contains(&tuple.day_1_based) {
        return None;
    }
    if tuple.hour_0_based >= 24 {
        return None;
    }
    if tuple.minute_0_based >= 60 {
        return None;
    }

    let year = u64::from(tuple.year_word);
    let month = u64::from(tuple.month_1_based);
    let day = u64::from(tuple.day_1_based);
    let hour = u64::from(tuple.hour_0_based);
    let minute = u64::from(tuple.minute_0_based);
    let absolute_counter =
        ((((year * 12 + month) * 28 + day).checked_sub(29)? * 24 + hour) * 60) + minute;
    u32::try_from(absolute_counter).ok()
}
