use chrono::{NaiveDate, NaiveTime, TimeDelta};

use crate::{tariff, Ampere, Kw, Kwh, Weekday};

impl tariff::v221::Restrictions {
    /// Return the restrictions split into time and energy categories.
    pub(super) fn restrictions_by_category(&self) -> (TimeRestrictions, EnergyRestrictions) {
        let tariff::v221::Restrictions {
            start_time,
            end_time,
            start_date,
            end_date,
            min_kwh,
            max_kwh,
            min_current,
            max_current,
            min_power,
            max_power,
            min_duration,
            max_duration,
            day_of_week,
            reservation: _,
        } = self;

        let time = TimeRestrictions {
            start_time: *start_time,
            end_time: *end_time,
            start_date: *start_date,
            end_date: *end_date,
            min_duration: *min_duration,
            max_duration: *max_duration,
            weekdays: WeekdaySet(day_of_week.clone()),
        };

        let energy = EnergyRestrictions {
            min_kwh: *min_kwh,
            max_kwh: *max_kwh,
            min_current: *min_current,
            max_current: *max_current,
            min_power: *min_power,
            max_power: *max_power,
        };

        (time, energy)
    }
}

#[derive(Debug)]
#[cfg_attr(test, derive(Default))]
pub(super) struct EnergyRestrictions {
    /// Minimum consumed energy in kWh, for example 20, valid from this amount of energy (inclusive) being used.
    pub min_kwh: Option<Kwh>,

    /// Maximum consumed energy in kWh, for example 50, valid until this amount of energy (exclusive) being used.
    pub max_kwh: Option<Kwh>,

    /// If the charging current is equal to or lower than this value, the associated `TariffElement` becomes inactive.
    pub min_current: Option<Ampere>,

    /// If the charging current is equal to or higher than this value, the associated `TariffElement` becomes inactive.
    pub max_current: Option<Ampere>,

    /// If the charging power is equal to or lower than this value, the associated `TariffElement` becomes inactive.
    pub min_power: Option<Kw>,

    /// If the charging power is equal to or higher than this value, the associated `TariffElement` becomes inactive.
    pub max_power: Option<Kw>,
}

#[derive(Debug)]
#[cfg_attr(test, derive(Default))]
pub(super) struct TimeRestrictions {
    /// The `Element` is valid from this time of day in local time.
    pub start_time: Option<NaiveTime>,

    /// The `Element` is valid until this time of day in local time.
    pub end_time: Option<NaiveTime>,

    /// The `Element` is valid from this date (inclusive) in local time.
    pub start_date: Option<NaiveDate>,

    /// The `Element` is valid until this date (exclusive) in local time.
    pub end_date: Option<NaiveDate>,

    /// Minimum duration in seconds the Charging Session MUST last (inclusive).
    ///
    /// When the duration of a Charging Session is longer than the defined value, this `TariffElement` is or becomes active.
    /// Before that moment, this `TariffElement` is not yet active.
    pub min_duration: Option<TimeDelta>,

    /// Maximum duration in seconds the Charging Session MUST last (exclusive).
    ///
    /// When the duration of a Charging Session is shorter than the defined value, this `TariffElement` is or becomes active.
    /// After that moment, this `TariffElement` is no longer active.
    pub max_duration: Option<TimeDelta>,

    /// Which day(s) of the week this `TariffElement` is active.
    pub weekdays: WeekdaySet,
}

#[derive(Debug, Default)]
pub(crate) struct WeekdaySet(pub Option<Vec<Weekday>>);

impl WeekdaySet {
    /// Returns true if there are no `Weekday`s listed or the given weekday is in the list.
    pub(super) fn contains(&self, weekday: chrono::Weekday) -> bool {
        let Some(weekdays) = &self.0 else {
            return true;
        };

        weekdays.contains(&weekday.into())
    }
}
