#!/usr/bin/env python3
"""CPU monitoring functionality."""

import platform
import re
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import Any, ClassVar, Dict, List, Optional, Tuple

import psutil


@dataclass
class CPUStats:
    """CPU statistics."""

    # CPU identification
    vendor: str  # CPU vendor (e.g., Intel, AMD)
    model: str  # CPU model name
    # Overall CPU utilization percentage
    total_percent: float
    # Per-core CPU utilization percentages
    core_percents: List[float]
    # Frequency information
    current_freq: float  # Current frequency in MHz
    min_freq: float  # Minimum frequency in MHz
    max_freq: float  # Maximum frequency in MHz
    # Temperature if available
    temperature: float  # CPU temperature in Celsius
    # Load averages
    load_1min: float
    load_5min: float
    load_15min: float
    # Telemetry provenance and extended frequency context
    frequency_source: str = ""
    temperature_source: str = ""
    policy_min_freq: float = 0.0  # In MHz
    policy_max_freq: float = 0.0  # In MHz
    hardware_min_freq: float = 0.0  # In MHz
    hardware_max_freq: float = 0.0  # In MHz

    _VENDOR_KEYS: ClassVar[Tuple[str, ...]] = (
        "vendor_id",
        "vendor",
        "model vendor",
        "cpu implementer",
        "system type",
        "platform",
    )
    _MODEL_KEYS: ClassVar[Tuple[str, ...]] = (
        "model name",
        "cpu model",
        "hardware",
        "processor",
        "model",
        "cpu",
    )
    _ARM_IMPLEMENTER_VENDOR_MAP: ClassVar[Dict[int, str]] = {
        # Synced with util-linux lscpu ARM vendor mapping.
        0x41: "ARM",
        0x42: "Broadcom",
        0x43: "Cavium",
        0x44: "DEC",
        0x46: "Fujitsu",
        0x48: "HiSilicon",
        0x49: "Infineon",
        0x4D: "Motorola",
        0x4E: "NVIDIA",
        0x50: "APM",
        0x51: "Qualcomm",
        0x53: "Samsung",
        0x56: "Marvell",
        0x61: "Apple",
        0x66: "Faraday",
        0x69: "Intel",
        0x70: "Phytium",
        0xC0: "Ampere",
    }

    @staticmethod
    def _parse_cpuinfo(cpuinfo: str) -> Dict[str, str]:
        """Parse /proc/cpuinfo into normalized first-seen key/value pairs."""
        parsed: Dict[str, str] = {}
        for line in cpuinfo.splitlines():
            if ":" not in line:
                continue
            key, value = line.split(":", 1)
            normalized_key = key.strip().lower()
            normalized_value = value.strip()
            if not normalized_key or not normalized_value:
                continue
            parsed.setdefault(normalized_key, normalized_value)
        return parsed

    @staticmethod
    def _safe_float(value: Any) -> float:
        """Convert arbitrary numeric-like values to float."""
        try:
            parsed = float(value)
        except (TypeError, ValueError):
            return 0.0
        if parsed < 0:
            return 0.0
        return parsed

    @staticmethod
    def _read_text(path: Path) -> str:
        """Read a text file and return stripped content."""
        try:
            return path.read_text(encoding="utf-8", errors="ignore").strip()
        except (OSError, UnicodeError):
            return ""

    @staticmethod
    def _read_sysctl_value(name: str) -> str:
        """Read a Darwin sysctl key using the command-line utility."""
        try:
            result = subprocess.run(
                ["sysctl", "-n", name],
                capture_output=True,
                text=True,
                timeout=1.5,
                check=False,
            )
        except Exception:
            return ""

        if result.returncode != 0:
            return ""
        return str(result.stdout).strip()

    @staticmethod
    def _read_sysctl_freq_mhz(name: str) -> float:
        """Read sysctl frequency keys expressed in Hz and convert to MHz."""
        raw = CPUStats._read_sysctl_value(name)
        if not raw:
            return 0.0

        match = re.search(r"([0-9][0-9_,]*)", raw)
        if not match:
            return 0.0

        token = match.group(1).replace("_", "").replace(",", "")
        try:
            hz = int(token, 10)
        except ValueError:
            return 0.0

        if hz <= 0:
            return 0.0
        return hz / 1_000_000.0

    @staticmethod
    def _read_khz_file_mhz(path: Path) -> float:
        """Read cpufreq sysfs values and normalize to MHz.

        Linux cpufreq interfaces are expected to report kHz, but some kernel/driver
        combinations have historically exposed MHz in selected files. Guard against
        that quirk so UI ranges remain sane.
        """
        raw = CPUStats._read_text(path)
        if not raw:
            return 0.0

        token = raw.split()[0]
        try:
            khz = float(token)
        except ValueError:
            return 0.0

        if khz <= 0:
            return 0.0
        if khz < 10_000:
            # Heuristic: treat tiny "kHz" values as already-MHz output from
            # kernels/drivers with mixed-unit regressions.
            return khz
        return khz / 1000.0

    @staticmethod
    def _parse_cpu_list_count(cpu_list: str) -> int:
        """Parse Linux cpulist syntax (e.g. '0-3,8,10-11') into CPU count."""
        tokens = (cpu_list or "").replace(",", " ").split()
        count = 0

        for token in tokens:
            if "-" in token:
                start_s, end_s = token.split("-", 1)
                if not (start_s.isdigit() and end_s.isdigit()):
                    continue
                start = int(start_s, 10)
                end = int(end_s, 10)
                if end < start:
                    start, end = end, start
                count += (end - start) + 1
                continue

            if token.isdigit():
                count += 1

        return count

    @staticmethod
    def _map_cpu_implementer_vendor(implementer: str) -> str:
        """Map ARM CPU implementer IDs to vendor labels."""
        raw_value = implementer.strip()
        try:
            code = int(raw_value, 0)
        except ValueError:
            return raw_value or "Unknown"

        vendor = CPUStats._ARM_IMPLEMENTER_VENDOR_MAP.get(code)
        if vendor:
            return vendor
        return f"Unknown ({raw_value})"

    @staticmethod
    def _infer_vendor_from_model(model: str) -> str:
        """Infer CPU vendor from model/brand text."""
        lowered = (model or "").strip().lower()
        if not lowered:
            return "Unknown"

        if "intel" in lowered:
            return "Intel"
        if any(token in lowered for token in ("amd", "ryzen", "epyc", "athlon")):
            return "AMD"
        if "apple" in lowered:
            return "Apple"
        if "qualcomm" in lowered:
            return "Qualcomm"
        if "ampere" in lowered:
            return "Ampere"
        if "arm" in lowered or "cortex" in lowered or "neoverse" in lowered:
            return "ARM"
        return "Unknown"

    @staticmethod
    def _looks_like_model(value: str) -> bool:
        """Filter out numeric-only processor index values."""
        return bool(value) and not re.fullmatch(r"\d+", value)

    @staticmethod
    def _extract_identity(cpuinfo: str, fallback_model: str) -> Tuple[str, str]:
        """Extract vendor/model from cpuinfo with cross-architecture key support."""
        fields = CPUStats._parse_cpuinfo(cpuinfo)

        vendor = ""
        for key in CPUStats._VENDOR_KEYS:
            value = fields.get(key, "")
            if not value:
                continue
            if key == "cpu implementer":
                vendor = CPUStats._map_cpu_implementer_vendor(value)
            else:
                vendor = value
            break
        if not vendor:
            vendor = "Unknown"

        model = ""
        for key in CPUStats._MODEL_KEYS:
            value = fields.get(key, "")
            if not value:
                continue
            if key == "processor" and not CPUStats._looks_like_model(value):
                continue
            model = value
            break

        if not model:
            architecture = fields.get("cpu architecture", "")
            cpu_part = fields.get("cpu part", "")
            if architecture and cpu_part:
                model = f"{architecture} part {cpu_part}"

        if not model:
            model = fallback_model or "Unknown"

        return vendor, model

    @staticmethod
    def _collect_linux_identity(fallback_model: str) -> Tuple[str, str]:
        """Collect vendor/model identity from Linux cpuinfo."""
        try:
            with open("/proc/cpuinfo", "r", encoding="utf-8", errors="ignore") as f:
                cpuinfo = f.read()
        except (IOError, OSError):
            return "Unknown", fallback_model or "Unknown"
        return CPUStats._extract_identity(cpuinfo, fallback_model)

    @staticmethod
    def _collect_darwin_identity(fallback_model: str) -> Tuple[str, str]:
        """Collect vendor/model identity from Darwin sysctl."""
        model = (
            CPUStats._read_sysctl_value("machdep.cpu.brand_string")
            or CPUStats._read_sysctl_value("hw.model")
            or fallback_model
            or "Unknown"
        )

        vendor = CPUStats._infer_vendor_from_model(model)
        if vendor == "Unknown":
            machine = platform.machine().lower()
            if machine.startswith(("arm", "aarch64")):
                vendor = "Apple"
        return vendor, model

    @staticmethod
    def _collect_identity(fallback_model: str) -> Tuple[str, str]:
        """Collect vendor/model with platform-appropriate probes."""
        system_name = platform.system().lower()
        if system_name == "linux":
            return CPUStats._collect_linux_identity(fallback_model)
        if system_name == "darwin":
            return CPUStats._collect_darwin_identity(fallback_model)

        model = fallback_model or "Unknown"
        vendor = CPUStats._infer_vendor_from_model(model)
        return vendor, model

    @staticmethod
    def _collect_linux_cpufreq_metrics() -> Dict[str, Any]:
        """Collect frequency metrics from Linux cpufreq sysfs."""
        cpufreq_root = Path("/sys/devices/system/cpu/cpufreq")
        if not cpufreq_root.exists():
            return {}

        policies = sorted(cpufreq_root.glob("policy[0-9]*"))
        if not policies:
            return {}

        policy_mins: List[float] = []
        policy_maxs: List[float] = []
        hardware_mins: List[float] = []
        hardware_maxs: List[float] = []
        current_weighted_sum = 0.0
        current_weight = 0
        drivers: set[str] = set()
        governors: set[str] = set()

        for policy in policies:
            related_cpus = (
                CPUStats._read_text(policy / "related_cpus")
                or CPUStats._read_text(policy / "affected_cpus")
            )
            cpu_weight = CPUStats._parse_cpu_list_count(related_cpus)
            if cpu_weight < 1:
                cpu_weight = 1

            current_candidates = [
                CPUStats._read_khz_file_mhz(policy / "cpuinfo_avg_freq"),
                CPUStats._read_khz_file_mhz(policy / "cpuinfo_cur_freq"),
                CPUStats._read_khz_file_mhz(policy / "scaling_cur_freq"),
            ]
            current_mhz = next(
                (value for value in current_candidates if value > 0.0), 0.0
            )
            if current_mhz > 0.0:
                current_weighted_sum += current_mhz * cpu_weight
                current_weight += cpu_weight

            policy_min = CPUStats._read_khz_file_mhz(policy / "scaling_min_freq")
            policy_max = CPUStats._read_khz_file_mhz(policy / "scaling_max_freq")
            if policy_min > 0.0:
                policy_mins.append(policy_min)
            if policy_max > 0.0:
                policy_maxs.append(policy_max)

            hw_min = CPUStats._read_khz_file_mhz(policy / "cpuinfo_min_freq")
            hw_max = CPUStats._read_khz_file_mhz(policy / "cpuinfo_max_freq")
            amd_pstate_max = CPUStats._read_khz_file_mhz(policy / "amd_pstate_max_freq")
            # Some kernels/documentation revisions describe this attribute in
            # CPPC percent space. Ignore implausibly low values for frequency.
            if amd_pstate_max >= 300.0:
                hw_max = max(hw_max, amd_pstate_max)

            if hw_min > 0.0:
                hardware_mins.append(hw_min)
            if hw_max > 0.0:
                hardware_maxs.append(hw_max)

            driver = CPUStats._read_text(policy / "scaling_driver")
            governor = CPUStats._read_text(policy / "scaling_governor")
            if driver:
                drivers.add(driver)
            if governor:
                governors.add(governor)

        policy_min = min(policy_mins) if policy_mins else 0.0
        policy_max = max(policy_maxs) if policy_maxs else 0.0
        hardware_min = min(hardware_mins) if hardware_mins else 0.0
        hardware_max = max(hardware_maxs) if hardware_maxs else 0.0
        current_mhz = (
            current_weighted_sum / current_weight if current_weight > 0 else 0.0
        )

        boost_active: Optional[bool] = None
        boost_text = CPUStats._read_text(cpufreq_root / "boost")
        if boost_text in {"0", "1"}:
            boost_active = boost_text == "1"

        intel_no_turbo = CPUStats._read_text(
            Path("/sys/devices/system/cpu/intel_pstate/no_turbo")
        )
        if intel_no_turbo in {"0", "1"}:
            boost_active = intel_no_turbo == "0"

        effective_min = hardware_min if hardware_min > 0 else policy_min
        if effective_min <= 0:
            effective_min = policy_min if policy_min > 0 else hardware_min

        effective_max_candidates = [value for value in (policy_max, hardware_max) if value > 0]
        effective_max = max(effective_max_candidates) if effective_max_candidates else 0.0
        if boost_active is False and policy_max > 0.0:
            effective_max = policy_max

        if effective_min > 0.0 and effective_max > 0.0 and effective_min > effective_max:
            effective_min, effective_max = effective_max, effective_min

        source_parts = ["linux-cpufreq"]
        if drivers:
            source_parts.append(f"driver={','.join(sorted(drivers))}")
        if governors:
            source_parts.append(f"governor={','.join(sorted(governors))}")
        if boost_active is not None:
            source_parts.append(f"boost={'on' if boost_active else 'off'}")

        return {
            "current": current_mhz,
            "effective_min": effective_min,
            "effective_max": effective_max,
            "policy_min": policy_min,
            "policy_max": policy_max,
            "hardware_min": hardware_min,
            "hardware_max": hardware_max,
            "source": ",".join(source_parts),
        }

    @staticmethod
    def _collect_darwin_freq_metrics() -> Dict[str, Any]:
        """Collect frequency metrics from Darwin sysctl when available."""
        current = CPUStats._read_sysctl_freq_mhz("hw.cpufrequency")
        min_freq = CPUStats._read_sysctl_freq_mhz("hw.cpufrequency_min")
        max_freq = CPUStats._read_sysctl_freq_mhz("hw.cpufrequency_max")

        if current <= 0.0 and min_freq <= 0.0 and max_freq <= 0.0:
            return {}

        effective_min = min_freq
        effective_max = max_freq
        if effective_max <= 0.0 and current > 0.0:
            effective_max = current

        return {
            "current": current,
            "effective_min": effective_min,
            "effective_max": effective_max,
            "policy_min": min_freq,
            "policy_max": max_freq,
            "hardware_min": min_freq,
            "hardware_max": max_freq,
            "source": "darwin-sysctl-nominal",
        }

    @staticmethod
    def _score_temperature_candidate(sensor_name: str, label: str, vendor: str) -> int:
        """Score temperature candidates for CPU relevance."""
        name = (sensor_name or "").strip().lower()
        label_l = (label or "").strip().lower()
        vendor_l = (vendor or "").strip().lower()
        score = 0

        if any(
            token in name
            for token in (
                "coretemp",
                "x86_pkg_temp",
                "peci",
                "k10temp",
                "zenpower",
                "cpu_thermal",
                "soc_thermal",
                "acpitz",
                "cpu",
            )
        ):
            score += 25

        if "tdie" in label_l:
            score += 110
        elif any(token in label_l for token in ("package", "physical id", "pkg", "die")):
            score += 90
        elif "tctl" in label_l:
            score += 65
        elif "core" in label_l:
            score += 50
        elif "cpu" in label_l:
            score += 40
        elif "soc" in label_l:
            score += 20

        if any(token in name for token in ("gpu", "pch", "nvme", "wifi", "iwlwifi")):
            score -= 60

        if "amd" in vendor_l:
            if "k10temp" in name or "zenpower" in name:
                score += 30
            if "tdie" in label_l:
                score += 30
        elif "intel" in vendor_l:
            if any(token in name for token in ("coretemp", "x86_pkg_temp", "peci")):
                score += 30
        elif any(token in vendor_l for token in ("apple", "arm", "qualcomm", "broadcom")):
            if any(token in name for token in ("cpu_thermal", "soc", "thermal_zone")):
                score += 30

        return score

    @staticmethod
    def _pick_temperature_from_psutil(
        temps: Dict[str, List[Any]], vendor: str
    ) -> Tuple[float, str, int]:
        """Pick the best CPU temperature candidate from psutil sensors."""
        best_temp = 0.0
        best_source = ""
        best_score = -10_000

        for sensor_name, entries in (temps or {}).items():
            for entry in entries or []:
                current = CPUStats._safe_float(getattr(entry, "current", 0.0))
                if current <= 0.0 or current > 200.0:
                    continue
                label = str(getattr(entry, "label", "") or "")
                score = CPUStats._score_temperature_candidate(sensor_name, label, vendor)
                if score > best_score:
                    best_score = score
                    best_temp = current
                    best_source = f"psutil:{sensor_name}"
                    if label:
                        best_source = f"{best_source}/{label}"

        if best_temp <= 0.0:
            return 0.0, "", -10_000
        return best_temp, best_source, best_score

    @staticmethod
    def _pick_temperature_from_linux_hwmon(vendor: str) -> Tuple[float, str, int]:
        """Pick the best CPU temperature candidate from Linux hwmon sysfs."""
        hwmon_root = Path("/sys/class/hwmon")
        if not hwmon_root.exists():
            return 0.0, "", -10_000

        best_temp = 0.0
        best_source = ""
        best_score = -10_000

        for hwmon_dir in sorted(hwmon_root.glob("hwmon*")):
            sensor_name = CPUStats._read_text(hwmon_dir / "name") or hwmon_dir.name
            for input_file in sorted(hwmon_dir.glob("temp*_input")):
                raw = CPUStats._read_text(input_file)
                if not raw:
                    continue
                try:
                    value = float(raw.split()[0])
                except ValueError:
                    continue
                if value <= 0:
                    continue

                temp_c = value / 1000.0 if abs(value) >= 1000.0 else value
                if temp_c <= 0.0 or temp_c > 200.0:
                    continue

                label_file = input_file.with_name(
                    input_file.name.replace("_input", "_label")
                )
                label = CPUStats._read_text(label_file)
                score = CPUStats._score_temperature_candidate(sensor_name, label, vendor)
                score += 10  # hwmon labels are generally high-quality

                if score > best_score:
                    best_score = score
                    best_temp = temp_c
                    best_source = f"hwmon:{sensor_name}"
                    if label:
                        best_source = f"{best_source}/{label}"

        if best_temp <= 0.0:
            return 0.0, "", -10_000
        return best_temp, best_source, best_score

    @staticmethod
    def _pick_temperature_from_linux_thermal_zone(vendor: str) -> Tuple[float, str, int]:
        """Pick CPU temperature from Linux thermal_zone sysfs fallback."""
        thermal_root = Path("/sys/class/thermal")
        if not thermal_root.exists():
            return 0.0, "", -10_000

        best_temp = 0.0
        best_source = ""
        best_score = -10_000

        for zone in sorted(thermal_root.glob("thermal_zone*")):
            zone_type = CPUStats._read_text(zone / "type") or zone.name
            raw_temp = CPUStats._read_text(zone / "temp")
            if not raw_temp:
                continue

            try:
                value = float(raw_temp.split()[0])
            except ValueError:
                continue
            if value <= 0:
                continue

            temp_c = value / 1000.0 if abs(value) >= 1000.0 else value
            if temp_c <= 0.0 or temp_c > 200.0:
                continue

            score = CPUStats._score_temperature_candidate(zone_type, zone_type, vendor)
            score += 5
            if score > best_score:
                best_score = score
                best_temp = temp_c
                best_source = f"thermal_zone:{zone_type}"

        if best_temp <= 0.0:
            return 0.0, "", -10_000
        return best_temp, best_source, best_score

    @staticmethod
    def get_stats() -> "CPUStats":
        """Get current CPU statistics.

        Returns:
            CPUStats object with current CPU information
        """
        system_name = platform.system().lower()

        # Get CPU utilization from one call for consistency.
        core_percents = psutil.cpu_percent(interval=None, percpu=True)
        total_percent = (
            sum(core_percents) / len(core_percents) if core_percents else 0.0
        )

        # Baseline frequency from psutil (MHz; non-Linux is nominal/fixed).
        freq = psutil.cpu_freq(percpu=False)
        if freq:
            current_freq = CPUStats._safe_float(freq.current)
            min_freq = CPUStats._safe_float(freq.min)
            max_freq = CPUStats._safe_float(freq.max)
        else:
            current_freq = min_freq = max_freq = 0.0
        frequency_source = ""
        if current_freq > 0.0:
            frequency_source = "psutil" if system_name == "linux" else "psutil-nominal"
        policy_min = 0.0
        policy_max = 0.0
        hardware_min = 0.0
        hardware_max = 0.0

        if system_name == "linux":
            linux_freq = CPUStats._collect_linux_cpufreq_metrics()
            if linux_freq:
                current_freq = CPUStats._safe_float(linux_freq.get("current", 0.0)) or current_freq
                min_freq = CPUStats._safe_float(linux_freq.get("effective_min", 0.0)) or min_freq
                max_freq = CPUStats._safe_float(linux_freq.get("effective_max", 0.0)) or max_freq
                policy_min = CPUStats._safe_float(linux_freq.get("policy_min", 0.0))
                policy_max = CPUStats._safe_float(linux_freq.get("policy_max", 0.0))
                hardware_min = CPUStats._safe_float(linux_freq.get("hardware_min", 0.0))
                hardware_max = CPUStats._safe_float(linux_freq.get("hardware_max", 0.0))
                frequency_source = str(linux_freq.get("source", "") or frequency_source)
        elif system_name == "darwin":
            darwin_freq = CPUStats._collect_darwin_freq_metrics()
            if darwin_freq:
                current_freq = CPUStats._safe_float(darwin_freq.get("current", 0.0)) or current_freq
                min_freq = CPUStats._safe_float(darwin_freq.get("effective_min", 0.0)) or min_freq
                max_freq = CPUStats._safe_float(darwin_freq.get("effective_max", 0.0)) or max_freq
                policy_min = CPUStats._safe_float(darwin_freq.get("policy_min", 0.0))
                policy_max = CPUStats._safe_float(darwin_freq.get("policy_max", 0.0))
                hardware_min = CPUStats._safe_float(darwin_freq.get("hardware_min", 0.0))
                hardware_max = CPUStats._safe_float(darwin_freq.get("hardware_max", 0.0))
                frequency_source = str(darwin_freq.get("source", "") or frequency_source)

        if min_freq <= 0.0 and policy_min > 0.0:
            min_freq = policy_min
        if max_freq <= 0.0:
            max_freq = max(policy_max, hardware_max, current_freq)
        if max_freq > 0.0 and current_freq > max_freq:
            max_freq = current_freq
        if min_freq > 0.0 and max_freq > 0.0 and min_freq > max_freq:
            min_freq, max_freq = max_freq, min_freq

        # Get CPU vendor and model information
        fallback_model = platform.processor() or platform.machine() or "Unknown"
        vendor, model = CPUStats._collect_identity(fallback_model)

        # Get CPU temperature with vendor-aware source ranking.
        temperature = 0.0
        temperature_source = ""
        best_temp_score = -10_000
        try:
            temps = psutil.sensors_temperatures()
            temp_value, temp_source, score = CPUStats._pick_temperature_from_psutil(
                temps,
                vendor,
            )
            if temp_value > 0.0 and score > best_temp_score:
                temperature = temp_value
                temperature_source = temp_source
                best_temp_score = score
        except (AttributeError, KeyError, TypeError):
            pass

        if system_name == "linux":
            temp_value, temp_source, score = CPUStats._pick_temperature_from_linux_hwmon(
                vendor
            )
            if temp_value > 0.0 and score > best_temp_score:
                temperature = temp_value
                temperature_source = temp_source
                best_temp_score = score

            temp_value, temp_source, score = (
                CPUStats._pick_temperature_from_linux_thermal_zone(vendor)
            )
            if temp_value > 0.0 and score > best_temp_score:
                temperature = temp_value
                temperature_source = temp_source
                best_temp_score = score

        # Get load averages normalized by logical CPU count.
        try:
            cpu_count = psutil.cpu_count(logical=True)
            if cpu_count is None or cpu_count < 1:
                cpu_count = 1

            raw_loads = psutil.getloadavg()
            if not raw_loads or len(raw_loads) != 3:
                load_1 = load_5 = load_15 = 0.0
            else:
                load_1, load_5, load_15 = [max(0.0, x / cpu_count) for x in raw_loads]
        except Exception:
            load_1 = load_5 = load_15 = 0.0

        return CPUStats(
            vendor=vendor,
            model=model,
            total_percent=total_percent,
            core_percents=core_percents,
            current_freq=current_freq,
            min_freq=min_freq,
            max_freq=max_freq,
            temperature=temperature,
            load_1min=load_1,
            load_5min=load_5,
            load_15min=load_15,
            frequency_source=frequency_source,
            temperature_source=temperature_source,
            policy_min_freq=policy_min,
            policy_max_freq=policy_max,
            hardware_min_freq=hardware_min,
            hardware_max_freq=hardware_max,
        )
