//! Types for parsing all tariff versions.

use crate::{
    define_enum_from_json, json,
    tariff::Warning,
    warning::{self, IntoCaveat as _},
    Enum, IntoEnum, Verdict,
};

/// Type of tariff component.
///
/// See: <https://github.com/ocpi/ocpi/blob/release-2.2.1-bugfixes/mod_tariffs.asciidoc#mod_tariffs_tariffdimensiontype_enum>.
/// See: <https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_tariffs.md#44-tariffdimensiontype-enum>.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub(crate) enum DimensionType {
    /// Defined in kWh, `step_size` multiplier: 1 Wh.
    Energy,
    /// Flat fee, no unit for `step_size`.
    Flat,
    /// Time not charging: defined in hours, `step_size` multiplier: 1 second.
    ParkingTime,
    /// Time charging: defined in hours, `step_size` multiplier: 1 second.
    Time,
}

impl IntoEnum for DimensionType {
    fn enum_from_str(s: &str) -> Enum<Self> {
        let dt = if s.eq_ignore_ascii_case("energy") {
            DimensionType::Energy
        } else if s.eq_ignore_ascii_case("flat") {
            DimensionType::Flat
        } else if s.eq_ignore_ascii_case("parking_time") {
            DimensionType::ParkingTime
        } else if s.eq_ignore_ascii_case("time") {
            DimensionType::Time
        } else {
            return Enum::Unknown(s.to_owned());
        };

        Enum::Known(dt)
    }
}

define_enum_from_json!(DimensionType, display_name: "DimensionType", warning_id: "dimension_type");

impl json::FromJson<'_> for u64 {
    type Warning = Warning;

    fn from_json(elem: &'_ json::Element<'_>) -> Verdict<Self, Self::Warning> {
        let warnings = warning::Set::new();
        let Some(s) = elem.as_number_str() else {
            return warnings.bail(
                elem,
                Warning::FieldInvalidType {
                    expected_type: json::ValueKind::Number,
                },
            );
        };

        let n = match s.parse::<u64>() {
            Ok(n) => n,
            Err(err) => {
                return warnings.bail(elem, Warning::field_invalid_value(s, err.to_string()));
            }
        };

        Ok(n.into_caveat(warnings))
    }
}
