use crate::common::{deserialize_thing_keys, serialize_thing_keys, RequiredValues};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Top-level POST body for a `historicData` request.
#[derive(Debug, Serialize, Deserialize)]
pub struct HistoricRequest {
    #[serde(rename = "historicData")]
    pub historic_data: HistoricCommand,
}

impl HistoricRequest {
    /// Request all available fields for `thing_keys` over the given 24-hour window.
    ///
    /// `start` / `end` must be ISO 8601 strings, e.g. `"2021-09-15T00:00:00.000Z"`.
    pub fn new(thing_keys: Vec<String>, start: impl Into<String>, end: impl Into<String>) -> Self {
        Self {
            historic_data: HistoricCommand {
                command: "historicData".to_owned(),
                params: HistoricParams {
                    thing_keys,
                    required_values: RequiredValues::All,
                    start: start.into(),
                    end: end.into(),
                },
            },
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct HistoricCommand {
    pub command: String,
    pub params: HistoricParams,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct HistoricParams {
    #[serde(
        rename = "thingKey",
        serialize_with = "serialize_thing_keys",
        deserialize_with = "deserialize_thing_keys"
    )]
    pub thing_keys: Vec<String>,
    #[serde(rename = "requiredValues")]
    pub required_values: RequiredValues,
    /// ISO 8601 start of the query window.
    pub start: String,
    /// ISO 8601 end of the query window (max 24 h after `start`).
    pub end: String,
}

/// Top-level response body for a `historicData` request.
#[derive(Debug, Serialize, Deserialize)]
pub struct HistoricResponse {
    #[serde(rename = "historicData")]
    pub historic_data: HistoricResult,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct HistoricResult {
    pub params: HistoricResultParams,
    pub success: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct HistoricResultParams {
    /// One entry per requested device. Each map has a single key: the device's `thingKey`.
    pub value: Vec<HashMap<String, HistoricDeviceData>>,
}

/// Time-series measurements for a single device over the queried window.
///
/// All numeric series share the same index as `ts`.
/// Fields not supported by the device type are `None`.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct HistoricDeviceData {
    /// ISO 8601 timestamps, one per sample.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ts: Option<Vec<String>>,

    #[serde(rename = "batteryCycletime", skip_serializing_if = "Option::is_none")]
    pub battery_cycletime: Option<Vec<f64>>,
    #[serde(rename = "batterySoC", skip_serializing_if = "Option::is_none")]
    pub battery_soc: Option<Vec<f64>>,
    #[serde(rename = "batteryCycletime2", skip_serializing_if = "Option::is_none")]
    pub battery_cycletime2: Option<Vec<f64>>,
    #[serde(rename = "batterySoC2", skip_serializing_if = "Option::is_none")]
    pub battery_soc2: Option<Vec<f64>>,

    #[serde(rename = "powerCharging", skip_serializing_if = "Option::is_none")]
    pub power_charging: Option<Vec<f64>>,
    #[serde(rename = "powerDischarging", skip_serializing_if = "Option::is_none")]
    pub power_discharging: Option<Vec<f64>>,
    #[serde(rename = "powerExporting", skip_serializing_if = "Option::is_none")]
    pub power_exporting: Option<Vec<f64>>,
    #[serde(rename = "powerImporting", skip_serializing_if = "Option::is_none")]
    pub power_importing: Option<Vec<f64>>,
    #[serde(rename = "powerConsuming", skip_serializing_if = "Option::is_none")]
    pub power_consuming: Option<Vec<f64>>,
    #[serde(rename = "powerAutoconsuming", skip_serializing_if = "Option::is_none")]
    pub power_autoconsuming: Option<Vec<f64>>,
    #[serde(rename = "powerGenerating", skip_serializing_if = "Option::is_none")]
    pub power_generating: Option<Vec<f64>>,
    #[serde(rename = "powerDC", skip_serializing_if = "Option::is_none")]
    pub power_dc: Option<Vec<f64>>,
    #[serde(rename = "currentDC", skip_serializing_if = "Option::is_none")]
    pub current_dc: Option<Vec<f64>>,
    #[serde(rename = "voltageDC", skip_serializing_if = "Option::is_none")]
    pub voltage_dc: Option<Vec<f64>>,
    #[serde(rename = "temperature", skip_serializing_if = "Option::is_none")]
    pub temperature: Option<Vec<f64>>,

    #[serde(rename = "energyCharging", skip_serializing_if = "Option::is_none")]
    pub energy_charging: Option<Vec<f64>>,
    #[serde(rename = "energyDischarging", skip_serializing_if = "Option::is_none")]
    pub energy_discharging: Option<Vec<f64>>,
    #[serde(rename = "energyExporting", skip_serializing_if = "Option::is_none")]
    pub energy_exporting: Option<Vec<f64>>,
    #[serde(rename = "energyImporting", skip_serializing_if = "Option::is_none")]
    pub energy_importing: Option<Vec<f64>>,
    #[serde(rename = "energyConsuming", skip_serializing_if = "Option::is_none")]
    pub energy_consuming: Option<Vec<f64>>,
    #[serde(rename = "energyAutoconsuming", skip_serializing_if = "Option::is_none")]
    pub energy_autoconsuming: Option<Vec<f64>>,
    #[serde(rename = "energyGenerating", skip_serializing_if = "Option::is_none")]
    pub energy_generating: Option<Vec<f64>>,

    #[serde(rename = "energyChargingTotal", skip_serializing_if = "Option::is_none")]
    pub energy_charging_total: Option<Vec<f64>>,
    #[serde(rename = "energyDischargingTotal", skip_serializing_if = "Option::is_none")]
    pub energy_discharging_total: Option<Vec<f64>>,
    #[serde(rename = "energyExportingTotal", skip_serializing_if = "Option::is_none")]
    pub energy_exporting_total: Option<Vec<f64>>,
    #[serde(rename = "energyImportingTotal", skip_serializing_if = "Option::is_none")]
    pub energy_importing_total: Option<Vec<f64>>,
    #[serde(rename = "energyConsumingTotal", skip_serializing_if = "Option::is_none")]
    pub energy_consuming_total: Option<Vec<f64>>,
    #[serde(rename = "energyGeneratingTotal", skip_serializing_if = "Option::is_none")]
    pub energy_generating_total: Option<Vec<f64>>,
}
