use crate::localization::{DurationUnit, Localizer};

pub(crate) fn format_relative_duration_rounded(localizer: Localizer, seconds: i64) -> String {
    let seconds = seconds.max(1);
    let minute = 60;
    let hour = 60 * minute;
    let day = 24 * hour;
    let week = 7 * day;
    let month = 30 * day;
    let year = 365 * day;

    if seconds < hour {
        format_duration_unit(localizer, ceil_div(seconds, minute), DurationUnit::Minute)
    } else if seconds < day {
        format_duration_unit(localizer, ceil_div(seconds, hour), DurationUnit::Hour)
    } else if seconds < week {
        format_duration_unit(localizer, ceil_div(seconds, day), DurationUnit::Day)
    } else if seconds < 8 * week {
        format_duration_unit(localizer, ceil_div(seconds, week), DurationUnit::Week)
    } else if seconds < 12 * month {
        format_duration_unit(localizer, ceil_div(seconds, month), DurationUnit::Month)
    } else {
        format_duration_unit(localizer, ceil_div(seconds, year), DurationUnit::Year)
    }
}

pub(crate) fn format_relative_duration_complete(localizer: Localizer, seconds: i64) -> String {
    let mut remaining = seconds.max(1);
    let day = 24 * 60 * 60;
    let hour = 60 * 60;
    let minute = 60;
    let units = [
        (DurationUnit::Day, day),
        (DurationUnit::Hour, hour),
        (DurationUnit::Minute, minute),
        (DurationUnit::Second, 1),
    ];
    let mut parts = Vec::new();

    for (unit, unit_seconds) in units {
        let value = remaining / unit_seconds;
        if value > 0 {
            parts.push(format_duration_unit(localizer, value, unit));
            remaining %= unit_seconds;
        }
    }

    parts.join(", ")
}

fn ceil_div(value: i64, divisor: i64) -> i64 {
    (value + divisor - 1) / divisor
}

fn format_duration_unit(localizer: Localizer, value: i64, unit: DurationUnit) -> String {
    format!(
        "{value} {}",
        localizer.duration_unit_label(unit, value == 1)
    )
}

pub(crate) fn format_stability_days(localizer: Localizer, days: f64) -> String {
    format!(
        "{days:.2} {}",
        localizer.duration_unit_label(DurationUnit::Day, false)
    )
}

pub(crate) fn format_unix_timestamp_utc(unix_seconds: i64) -> String {
    let days = unix_seconds.div_euclid(24 * 60 * 60);
    let seconds_of_day = unix_seconds.rem_euclid(24 * 60 * 60);
    let (year, month, day) = civil_from_days(days);
    let hour = seconds_of_day / 3600;
    let minute = (seconds_of_day % 3600) / 60;
    let second = seconds_of_day % 60;
    format!("{year:04}-{month:02}-{day:02} {hour:02}:{minute:02}:{second:02} UTC")
}

pub(crate) fn format_unix_timestamp_file_utc(unix_seconds: i64) -> String {
    let days = unix_seconds.div_euclid(24 * 60 * 60);
    let seconds_of_day = unix_seconds.rem_euclid(24 * 60 * 60);
    let (year, month, day) = civil_from_days(days);
    let hour = seconds_of_day / 3600;
    let minute = (seconds_of_day % 3600) / 60;
    format!("{year:04}-{month:02}-{day:02}-{hour:02}-{minute:02}")
}

fn civil_from_days(days_since_unix_epoch: i64) -> (i64, i64, i64) {
    let z = days_since_unix_epoch + 719_468;
    let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
    let day_of_era = z - era * 146_097;
    let year_of_era =
        (day_of_era - day_of_era / 1_460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
    let mut year = year_of_era + era * 400;
    let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
    let month_prime = (5 * day_of_year + 2) / 153;
    let day = day_of_year - (153 * month_prime + 2) / 5 + 1;
    let month = month_prime + if month_prime < 10 { 3 } else { -9 };
    if month <= 2 {
        year += 1;
    }
    (year, month, day)
}
