import json
import logging
import time
from dataclasses import dataclass

from test_automation_suite.utility.cmd import RunCmdResult, run_cmd

logger = logging.getLogger(__name__)


@dataclass
class SingleFanData:
    temperature: float
    speed: float


@dataclass
class FanData:
    cpu: SingleFanData
    gpu: SingleFanData | None = None


class TCCD:
    # Some devices require additional time after enabling
    # sensor collection before fan data becomes available.
    SENSOR_COLLECTION_WAIT_TIME = 3

    @staticmethod
    def _call_dbus_method(method: str, parameters: list[str] | None = None) -> RunCmdResult:
        return run_cmd(
            [
                "gdbus",
                "call",
                "--system",
                "--dest",
                "com.tuxedocomputers.tccd",
                "--object-path",
                "/com/tuxedocomputers/tccd",
                "--method",
                method,
            ]
            + (parameters or [])
        )

    @staticmethod
    def get_fan_data() -> FanData:
        """Retrieve and parse fan data from GetFanDataJSON.

        Returns the CPU and dGPU temperatures and fan speeds.

        NOTE:
            GetFanDataJSON returns both gpu1 and gpu2.
            Only gpu1 (the dGPU fan) is used.
            gpu2 can be ignored because there used to be models
            where the GPU had two fans.
        """
        logger.debug("Get Fan Data")
        TCCD._enable_fan_data_collection(True)
        time.sleep(TCCD.SENSOR_COLLECTION_WAIT_TIME)
        cmd = TCCD._call_dbus_method("com.tuxedocomputers.tccd.GetFanDataJSON")
        time.sleep(TCCD.SENSOR_COLLECTION_WAIT_TIME)
        TCCD._enable_fan_data_collection(False)
        if not cmd.success:
            raise Exception("failed to call tccd dbus api")
        json_string = cmd.stdout[2:-4]
        data = json.loads(json_string)
        cpu_fan_data = SingleFanData(
            speed=data["cpu"]["speed"]["data"],
            temperature=data["cpu"]["temp"]["data"],
        )
        gpu_fan_data = SingleFanData(
            speed=data["gpu1"]["speed"]["data"],
            temperature=data["gpu1"]["temp"]["data"],
        )
        return FanData(cpu=cpu_fan_data, gpu=gpu_fan_data)

    @staticmethod
    def _enable_fan_data_collection(enabled: bool) -> None:
        """Enable sensor data collection.

        Fan speed data is only available when sensor data collection
        is enabled via SetSensorDataCollectionStatus.
        Otherwise,the fan speed is -1.
        """

        logger.debug("Activate the Sensors")
        cmd = TCCD._call_dbus_method(
            "com.tuxedocomputers.tccd.SetSensorDataCollectionStatus",
            ["true" if enabled else "false"],
        )
        if not cmd.success:
            raise Exception("failed to call tccd dbus api")
