"""Hardware-PWM backend for Raspberry Pi 5 generator sweep gating."""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path


_PWM_CHANNEL_BY_PIN = {
    12: 0,
    13: 1,
    18: 2,
    19: 3,
}


@dataclass(slots=True)
class HardwarePwmGate:
    """Thin wrapper around Raspberry Pi hardware PWM output."""

    pin: int
    frequency_hz: int
    duty_cycle: float
    _pwm: object | None = field(init=False, default=None, repr=False)
    _running: bool = field(init=False, default=False, repr=False)

    def __post_init__(self) -> None:
        """Validate static PWM configuration."""
        if self.pin not in _PWM_CHANNEL_BY_PIN:
            raise ValueError(
                "generator_sweep.pwm_pin must be one of GPIO12, GPIO13, GPIO18, or GPIO19 on Raspberry Pi 5"
            )
        if self.frequency_hz <= 0:
            raise ValueError("generator_sweep.pwm_frequency_hz must be > 0")
        if not (0.0 < self.duty_cycle <= 1.0):
            raise ValueError("generator_sweep.pwm_duty_cycle must be within (0, 1]")

    def open(self) -> None:
        """Initialize hardware PWM handle."""
        if self._pwm is not None:
            return

        try:
            from rpi_hardware_pwm import HardwarePWM
        except ImportError as exc:
            raise RuntimeError(
                "rpi_hardware_pwm is required for generator_sweep PWM output. "
                "Install project dependencies on the Raspberry Pi before running this script."
            ) from exc

        channel = _PWM_CHANNEL_BY_PIN[self.pin]
        try:
            self._pwm = HardwarePWM(pwm_channel=channel, hz=self.frequency_hz, chip=0)
        except Exception as exc:
            config_hint = _boot_config_hint()
            raise RuntimeError(
                "Failed to initialize Raspberry Pi hardware PWM. "
                f"Enable the PWM overlay in {config_hint} by adding "
                "'dtoverlay=pwm-2chan', then reboot the Raspberry Pi."
            ) from exc
        self._running = False

    def enable(self) -> None:
        """Start PWM output when not already running."""
        if self._pwm is None:
            raise RuntimeError("Hardware PWM gate is not open")
        if self._running:
            return

        duty_percent = self.duty_cycle * 100.0
        self._pwm.start(duty_percent)
        self._running = True

    def disable(self) -> None:
        """Stop PWM output when active."""
        if self._pwm is None or not self._running:
            return
        self._pwm.stop()
        self._running = False

    def close(self) -> None:
        """Stop PWM and release runtime state."""
        self.disable()
        self._pwm = None


def _boot_config_hint() -> str:
    """Return the most likely active Raspberry Pi boot config path."""
    firmware_config = Path("/boot/firmware/config.txt")
    if firmware_config.exists():
        return str(firmware_config)
    return "/boot/config.txt"
