use alumet::{
    metrics::{TypedMetricId, error::MetricCreationError},
    plugin::AlumetPluginStart,
    units::{PrefixedUnit, Unit},
};

use crate::amd::utils::{
    METRIC_LABEL_ACTIVITY, METRIC_LABEL_ENERGY, METRIC_LABEL_MEMORY, METRIC_LABEL_POWER,
    METRIC_LABEL_PROCESS_COMPUTE_UNIT_OCCUPANCY, METRIC_LABEL_PROCESS_CPU, METRIC_LABEL_PROCESS_ENCODE,
    METRIC_LABEL_PROCESS_GFX, METRIC_LABEL_PROCESS_GTT, METRIC_LABEL_PROCESS_MEMORY, METRIC_LABEL_PROCESS_VRAM,
    METRIC_LABEL_TEMPERATURE, METRIC_LABEL_VOLTAGE,
};

/// Contains the ids of the measured metrics.
#[derive(Clone)]
pub struct Metrics {
    /// Metric type based on GPU activity usage data.
    pub gpu_activity_usage: TypedMetricId<f64>,
    /// Metric type based on GPU energy consumption data.
    pub gpu_energy_consumption: TypedMetricId<f64>,
    /// Metric type based on GPU used memory data.
    pub gpu_memory_usages: TypedMetricId<u64>,
    /// Metric type based on GPU electric power consumption data.
    pub gpu_power_consumption: TypedMetricId<u64>,
    /// Metric type based on GPU temperature data.
    pub gpu_temperatures: TypedMetricId<u64>,
    /// Metric type based on GPU socket voltage data.
    pub gpu_voltage: TypedMetricId<u64>,
    /// Metric type based on GPU process CU occupancy data.
    pub process_cu_occupancy: TypedMetricId<f64>,
    /// Metric type based on GPU process memory usage data.
    pub process_memory_usage: TypedMetricId<u64>,
    /// Metric type based on GPU GFX engine usage data.
    pub process_engine_usage_gfx: TypedMetricId<u64>,
    /// Metric type based on GPU encode engine usage data.
    pub process_engine_usage_encode: TypedMetricId<u64>,
    /// Metric type based on GTT GPU process memory usage data.
    pub process_memory_usage_gtt: TypedMetricId<u64>,
    /// Metric type based on CPU GPU process memory usage data.
    pub process_memory_usage_cpu: TypedMetricId<u64>,
    /// Metric type based on VRAM GPU process memory usage data.
    pub process_memory_usage_vram: TypedMetricId<u64>,
}

impl Metrics {
    pub fn new(alumet: &mut AlumetPluginStart) -> Result<Self, MetricCreationError> {
        Ok(Self {
            gpu_activity_usage: alumet.create_metric::<f64>(
                METRIC_LABEL_ACTIVITY,
                Unit::Percent,
                "Get GPU activity usage in percentage",
            )?,
            gpu_energy_consumption: alumet.create_metric::<f64>(
                METRIC_LABEL_ENERGY,
                PrefixedUnit::milli(Unit::Joule),
                "Get GPU energy consumption in millijoule",
            )?,
            gpu_memory_usages: alumet.create_metric::<u64>(
                METRIC_LABEL_MEMORY,
                Unit::Byte,
                "Get GPU used memory in byte",
            )?,
            gpu_power_consumption: alumet.create_metric::<u64>(
                METRIC_LABEL_POWER,
                Unit::Watt,
                "Get GPU electric average power consumption in watts",
            )?,
            gpu_temperatures: alumet.create_metric::<u64>(
                METRIC_LABEL_TEMPERATURE,
                Unit::DegreeCelsius,
                "Get GPU temperature in °C",
            )?,
            gpu_voltage: alumet.create_metric::<u64>(
                METRIC_LABEL_VOLTAGE,
                PrefixedUnit::milli(Unit::Volt),
                "Get GPU voltage in millivolt",
            )?,
            process_cu_occupancy: alumet.create_metric::<f64>(
                METRIC_LABEL_PROCESS_COMPUTE_UNIT_OCCUPANCY,
                Unit::Percent,
                "Get compute units used by a process in percentage",
            )?,
            process_memory_usage: alumet.create_metric::<u64>(
                METRIC_LABEL_PROCESS_MEMORY,
                Unit::Byte,
                "Get process memory usage in byte",
            )?,
            process_engine_usage_encode: alumet.create_metric::<u64>(
                METRIC_LABEL_PROCESS_ENCODE,
                PrefixedUnit::nano(Unit::Second),
                "Get process encode engine usage in nanosecond",
            )?,
            process_engine_usage_gfx: alumet.create_metric::<u64>(
                METRIC_LABEL_PROCESS_GFX,
                PrefixedUnit::nano(Unit::Second),
                "Get process GFX engine usage in nanosecond",
            )?,
            process_memory_usage_gtt: alumet.create_metric::<u64>(
                METRIC_LABEL_PROCESS_GTT,
                Unit::Byte,
                "Get process GTT memory usage in byte",
            )?,
            process_memory_usage_cpu: alumet.create_metric::<u64>(
                METRIC_LABEL_PROCESS_CPU,
                Unit::Byte,
                "Get process CPU memory usage in byte",
            )?,
            process_memory_usage_vram: alumet.create_metric::<u64>(
                METRIC_LABEL_PROCESS_VRAM,
                Unit::Byte,
                "Get process VRAM memory usage in byte",
            )?,
        })
    }
}
