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

/// Top-level POST body for a `realtimeData` request.
#[derive(Debug, Serialize, Deserialize)]
pub struct RealtimeRequest {
    #[serde(rename = "realtimeData")]
    pub realtime_data: RealtimeCommand,
}

impl RealtimeRequest {
    /// Request all available realtime fields for the given devices.
    pub fn new(thing_keys: Vec<String>) -> Self {
        Self {
            realtime_data: RealtimeCommand {
                command: "realtimeData".to_owned(),
                params: RealtimeParams {
                    thing_keys,
                    required_values: RequiredValues::All,
                },
            },
        }
    }
}

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

#[derive(Debug, Serialize, Deserialize)]
pub struct RealtimeParams {
    #[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,
}

/// Top-level response body for a `realtimeData` request.
#[derive(Debug, Serialize, Deserialize)]
pub struct RealtimeResponse {
    #[serde(rename = "realtimeData")]
    pub realtime_data: RealtimeResult,
}

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

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

/// Latest snapshot measurements for a single device.
///
/// Fields not supported by the device type are `None`.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct RealtimeDeviceData {
    /// ISO 8601 timestamp of the last received packet.
    #[serde(rename = "lastUpdate", skip_serializing_if = "Option::is_none")]
    pub last_update: Option<String>,
    /// ISO 8601 timestamp of the first packet ever received.
    #[serde(rename = "thingFind", skip_serializing_if = "Option::is_none")]
    pub thing_find: Option<String>,

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

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

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

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