# Copyright 2025 Acute Technology Inc. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""Abstract definition of all instances within Protocol Exerciser."""

from abc import ABC, abstractmethod
from collections.abc import Callable
from functools import cached_property
from typing import TYPE_CHECKING, Any, ClassVar

import betterproto

if TYPE_CHECKING:
    from aqpxlib.toplevel import AqProtocolExerciser


class PxAbstractInstance(ABC):
    """Base class for all instances."""

    # Used to uniquely identify the class
    identifier: str = ""
    protobuf_key: str = ""

    # Event handlers table
    # For PxAbstractDevice, the following properties are used:
    #   _protobuf_event_cls: the protobuf class containing the event types
    #   _protobuf_available_event_fields: a list of event types that the device can handle
    _protobuf_event_cls: type[betterproto.Message] = None
    _protobuf_event_cls_tag: int = 0
    _protobuf_available_event_fields: ClassVar[list[str]] = []

    @cached_property
    def available_events_map(self):
        """A reverse mapping of `field_name_by_number`, which uses field name as key."""
        return {
            v: f"{k:08x}"
            for k, v in self._protobuf_event_cls._betterproto.field_name_by_number.items()  # noqa: SLF001
            if v in self._protobuf_available_event_fields
        }

    def __init__(self, exerciser: "AqProtocolExerciser") -> None:
        """Initialize the abstract instance."""
        self.exerciser = exerciser

    @abstractmethod
    def add_event_handler(self, event: str, handler: Callable[[Any], Any]):
        """Add an event handler to the instance."""

    def remove_event_handler(self, event: str):
        """Remove an event handler."""
        self.exerciser.remove_event_handler(event)

    def get_event_handlers(
        self,
        filter_by: Callable[[str], bool] | None = None,
    ) -> dict[str, Callable]:
        """Get all available event handlers."""
        return self.exerciser.get_event_handlers(filter_by)

    def on_event(
        self,
        event_type: str,
    ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
        """Add an event handler to the instance by decorator."""

        def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
            self.add_event_handler(event_type, func)
            return func

        return decorator
