from __future__ import annotations

import dataclasses
from typing import Any


class Shocker:
    """Base class for :class:`HTTPShocker <pishock.zap.httpapi.HTTPShocker>` and
    :class:`SerialShocker <pishock.zap.serialapi.SerialShocker>`.

    Applications which only need access to
    :meth:`shock() <pishock.zap.httpapi.HTTPShocker.shock()>`,
    :meth:`vibrate() <pishock.zap.httpapi.HTTPShocker.vibrate()>`,
    :meth:`beep() <pishock.zap.httpapi.HTTPShocker.beep()>`, and
    :meth:`info() <pishock.zap.httpapi.HTTPShocker.info()>` (with
    :class:`BasicShockerInfo <pishock.zap.core.BasicShockerInfo>` only) can swap out a
    :class:`HTTPShocker <pishock.zap.httpapi.HTTPShocker>` for a
    :class:`SerialShocker <pishock.zap.serialapi.SerialShocker>` (with only
    initialization changing) to support both APIs.
    """

    IS_SERIAL: bool

    def shock(self, *, duration: int | float, intensity: int) -> None:
        raise NotImplementedError  # pragma: no cover

    def vibrate(self, *, duration: int | float, intensity: int) -> None:
        raise NotImplementedError  # pragma: no cover

    def beep(self, duration: int | float) -> None:
        raise NotImplementedError  # pragma: no cover

    def info(self) -> BasicShockerInfo:
        raise NotImplementedError  # pragma: no cover


@dataclasses.dataclass
class BasicShockerInfo:
    """Basic information about a shocker.

    Used by :meth:`PiShockAPI.get_shockers() <pishock.zap.httpapi.PiShockAPI.get_shockers()>` and
    :meth:`SerialShocker.info() <pishock.zap.serialapi.SerialShocker.info()>`. Calling
    :meth:`HTTPShocker.info() <pishock.zap.httpapi.HTTPShocker.info()>` returns a
    :class:`httpapi.DetailedShockerInfo
    <pishock.zap.httpapi.DetailedShockerInfo>` instance instead.

    Attributes:
        name: The name of this shocker in the web interface (or an autogenerated
              name for serial shockers).
        client_id: The ID of the PiShock this shocker belongs to.
        shocker_id: The ID of this shocker.
        is_paused: Whether the shocker is currently paused.
    """

    name: str
    client_id: int
    shocker_id: int
    is_paused: bool

    @classmethod
    def from_get_shockers_api_dict(
        cls, data: dict[str, Any], client_id: int
    ) -> BasicShockerInfo:
        return cls(
            name=data["name"],
            client_id=client_id,
            shocker_id=data["id"],
            is_paused=data["paused"],
        )
