from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Callable, Protocol

from unmake.utils.validation import sanitize_op_type, validate_payload


class UndoableOperation(Protocol):
    def undo(self) -> None: ...


OperationFactory = Callable[[dict[str, Any]], UndoableOperation]


@dataclass(slots=True)
class OperationRegistry:
    _factories: dict[str, OperationFactory] = field(default_factory=dict)

    def register(self, op_type: str, factory: OperationFactory) -> None:
        normalized = sanitize_op_type(op_type)
        self._factories[normalized] = factory

    def build(self, op_type: str, payload: dict[str, Any]) -> UndoableOperation:
        normalized = sanitize_op_type(op_type)
        validate_payload(payload)
        try:
            factory = self._factories[normalized]
        except KeyError as exc:
            available = ", ".join(sorted(self._factories.keys())) or "<none>"
            raise KeyError(
                f"No registered factory for operation type: '{normalized}'. "
                f"Available types: {available}"
            ) from exc
        return factory(payload)
