from abc import ABC, abstractmethod
from typing import Callable


class AlarmStyle(ABC):
    """A pluggable alarm behaviour.

    A style owns *its own ringing tone*: it uses the audio engine (pygame)
    directly and decides when the alarm stops and what the LED does. It never
    touches hardware directly: ``set_led`` is injected by the runner, and the
    runner owns the audio engine lifecycle (mixer init/quit). Styles stay
    import-safe in the API process — ``import pygame`` does not initialise
    audio, so the API can import styles just to validate names.

    Lifecycle: construct once with ``set_led``/``music_file``; call ``start()``
    to begin ringing (kick off the tone and initial LED); then call ``update``
    every tick. Return ``False`` from ``update`` to stop the alarm.
    """

    def __init__(
        self, set_led: Callable[[bool], None], music_file: str | None = None
    ):
        self.set_led = set_led
        self.music_file = music_file

    def start(self) -> None:
        """Begin ringing. Override to start the style's tone / initial LED."""

    @abstractmethod
    def update(self, now: float, is_pressed: bool) -> bool:
        """Advance the style by one tick.

        Args:
            now: current time (``time.time()`` from the runner).
            is_pressed: whether the button is currently held down.

        Returns:
            True to keep ringing, False to stop.
        """
