use chrono::DateTime;
use serde::Serialize;

use crate::{
    Actual, Language, PartialPowerAverage, PowerAverage, PowerTariffMatches,
    costs::{CostPeriods, CostPeriodsSimple},
    hours::Hours,
};

#[derive(Debug, Clone, Copy, Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PowerDivide {
    pub(crate) hours: Option<Hours>,
    divider: u8,
}

impl PowerDivide {
    pub const fn new(hours: Option<Hours>, divider: u8) -> Self {
        Self { hours, divider }
    }

    pub fn hours(&self) -> Option<Hours> {
        self.hours
    }

    pub fn divider<Tz: chrono::TimeZone>(&self, timestamp: DateTime<Tz>) -> u8 {
        if let Some(hours) = self.hours()
            && hours.matches(timestamp)
        {
            self.divider
        } else {
            1
        }
    }

    pub fn multiplier<Tz: chrono::TimeZone>(&self, timestamp: DateTime<Tz>) -> f64 {
        1. / self.divider(timestamp) as f64
    }
}

#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum PowerTariff {
    Unverified,
    NotImplemented,
    Periods {
        method: TariffCalculationMethod,
        /// Divide kw by this amount during these hours
        power_divide: Option<PowerDivide>,
        periods: CostPeriods,
    },
}

impl PowerTariff {
    pub const fn new(method: TariffCalculationMethod, periods: CostPeriods) -> Self {
        Self::Periods {
            method,
            periods,
            power_divide: None,
        }
    }

    pub const fn add_power_divide(self, power_divide: PowerDivide) -> Self {
        match self {
            Self::Unverified => unimplemented!(),
            Self::NotImplemented => unimplemented!(),
            Self::Periods {
                method,
                power_divide: _,
                periods,
            } => Self::Periods {
                method,
                power_divide: Some(power_divide),
                periods,
            },
        }
    }

    pub const fn is_unverified(&self) -> bool {
        matches!(self, Self::Unverified)
    }

    pub fn simplified(
        &self,
        fuse_size: u16,
        yearly_consumption: u32,
        language: Language,
    ) -> PowerTariffSimplified {
        PowerTariffSimplified::new(self, fuse_size, yearly_consumption, language)
    }

    pub fn periods(
        &self,
        averages: Vec<PowerAverage<Actual>>,
        current_power_average: Option<PartialPowerAverage>,
    ) -> PowerTariffMatches {
        match self {
            PowerTariff::Unverified => PowerTariffMatches::new_dummy(),
            PowerTariff::NotImplemented => PowerTariffMatches::new_dummy(),
            PowerTariff::Periods {
                method,
                periods,
                power_divide,
            } => PowerTariffMatches::new(
                *method,
                *power_divide,
                *periods,
                &averages.iter().map(|a| a.into_virtual(*power_divide)).collect::<Vec<_>>(),
                current_power_average,
            ),
        }
    }
}

/// The method used to calculate power tariffs
#[derive(Debug, Clone, Copy, Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum TariffCalculationMethod {
    /// Power peak for top hour of the top x days of the month
    AverageDays(u8),
    /// Average of top x hours of the month
    AverageHours(u8),
}

/// Like PowerTariff, but with costs being simple Money objects
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum PowerTariffSimplified {
    Unverified,
    NotImplemented,
    Periods {
        method: TariffCalculationMethod,
        /// Divide kw by this amount during these hours
        power_divide: Option<PowerDivide>,
        periods: CostPeriodsSimple,
    },
}

impl PowerTariffSimplified {
    fn new(fee: &PowerTariff, fuse_size: u16, yearly_consumption: u32, language: Language) -> Self {
        match *fee {
            PowerTariff::Unverified => PowerTariffSimplified::Unverified,
            PowerTariff::NotImplemented => PowerTariffSimplified::NotImplemented,
            PowerTariff::Periods {
                method,
                periods,
                power_divide,
            } => PowerTariffSimplified::Periods {
                method,
                power_divide,
                periods: CostPeriodsSimple::new(periods, fuse_size, yearly_consumption, language),
            },
        }
    }
}
