Framework for embedding localizations into Rust types
use crate::{Context, Localize};

use fixed_decimal::Decimal;
use jiff::{SpanRound, Timestamp, Unit, tz::TimeZone};
use writeable::Writeable;

impl Localize for Timestamp {
    fn localize(&self, context: &Context, buffer: &mut String) {
        // Get the current time
        let current_timestamp = Self::now();
        let current_datetime = current_timestamp.to_zoned(TimeZone::UTC).datetime();

        // Calculate the difference, rounded to the largest unit
        let unformatted_span = current_timestamp
            .until(*self)
            .unwrap()
            // Make sure the span is rounded to the largest available unit
            .round(
                SpanRound::new()
                    .largest(Unit::Year)
                    .relative(current_datetime),
            )
            .unwrap();

        // Find the largest "component": year, month, week etc
        let units: [(Unit, i64); 7] = [
            (Unit::Year, i64::from(unformatted_span.get_years())),
            (Unit::Month, i64::from(unformatted_span.get_months())),
            (Unit::Week, i64::from(unformatted_span.get_weeks())),
            (Unit::Day, i64::from(unformatted_span.get_days())),
            (Unit::Hour, i64::from(unformatted_span.get_hours())),
            (Unit::Minute, unformatted_span.get_minutes()),
            (Unit::Second, unformatted_span.get_seconds()),
        ];

        // Use the largest non-zero unit
        let selected_unit = units
            .iter()
            .find(|(_unit, value)| value.abs() > 0)
            .map_or(Unit::Second, |(unit, _value)| *unit);

        // Round the span to that selected unit
        let rounding_options = SpanRound::new()
            .smallest(selected_unit)
            .largest(selected_unit)
            .relative(current_datetime);
        let formatted_span = current_timestamp
            .until(*self)
            .unwrap()
            .round(rounding_options)
            .unwrap();

        // We can finally get the actual rounded value
        let selected_value = match selected_unit {
            Unit::Year => i64::from(formatted_span.get_years()),
            Unit::Month => i64::from(formatted_span.get_months()),
            Unit::Week => i64::from(formatted_span.get_weeks()),
            Unit::Day => i64::from(formatted_span.get_days()),
            Unit::Hour => i64::from(formatted_span.get_hours()),
            Unit::Minute => formatted_span.get_minutes(),
            Unit::Second => formatted_span.get_seconds(),
            _ => unreachable!(),
        };

        let formatter = context.relative_time_formatter(selected_unit).unwrap();

        let decimal = Decimal::from(selected_value);
        let formatted_text = formatter.format(decimal);

        formatted_text.write_to(buffer).unwrap();
    }
}