"""Rule type registry and base classes for pluggable rule evaluation.

Design Goals:
    - Enable new rule types without schema changes
    - Support plugin-based extensibility
    - Provide type-safe rule configuration
    - Allow gradual migration from simple to conditional rules
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, ClassVar, Protocol

if TYPE_CHECKING:
    from noteflow.domain.entities.meeting import Meeting
    from noteflow.domain.identity.entities import User, Workspace


@dataclass
class RuleContext:
    """Context available during rule evaluation.

    Provides access to domain entities and extensible metadata
    for rule condition evaluation.

    Attributes:
        meeting: The meeting being evaluated (if applicable).
        calendar_event: Calendar event context (if applicable).
        workspace: The workspace context.
        project: The project context.
        user: The user performing the operation.
        extra: Extensible context for custom rule types.
    """

    meeting: Meeting | None = None
    calendar_event: object | None = None  # CalendarEvent when available
    workspace: Workspace | None = None
    project: object | None = None  # Project when available
    user: User | None = None
    extra: dict[str, object] = field(default_factory=dict)


@dataclass
class RuleResult:
    """Result of rule evaluation.

    Attributes:
        matched: Whether the rule condition matched.
        actions: List of actions to execute.
        metadata: Additional result metadata for debugging/logging.
    """

    matched: bool
    actions: list[object] = field(default_factory=list)  # RuleAction when imported
    metadata: dict[str, object] = field(default_factory=dict)


class RuleEvaluator(Protocol):
    """Protocol for rule evaluation."""

    def evaluate(self, context: RuleContext) -> RuleResult:
        """Evaluate the rule against the given context.

        Args:
            context: The evaluation context.

        Returns:
            The evaluation result.
        """
        ...


class RuleType(ABC):
    """Base class for pluggable rule types.

    Subclasses implement specific rule behavior (export, trigger, retention, etc.).
    Register with @RuleTypeRegistry.register decorator.

    Example:
        @RuleTypeRegistry.register
        class RetentionRuleType(RuleType):
            name = "retention"
            version = 1

            def evaluate(self, rule_config, context):
                # Implementation
                ...
    """

    name: ClassVar[str]
    version: ClassVar[int] = 1

    @abstractmethod
    def evaluate(
        self, rule_config: dict[str, object], context: RuleContext
    ) -> RuleResult:
        """Evaluate this rule type against the given context.

        Args:
            rule_config: Rule-specific configuration dictionary.
            context: The evaluation context.

        Returns:
            The evaluation result.
        """
        ...

    @abstractmethod
    def validate_config(self, config: dict[str, object]) -> list[str]:
        """Validate rule configuration.

        Args:
            config: Configuration to validate.

        Returns:
            List of validation error messages (empty if valid).
        """
        ...

    def get_schema(self) -> dict[str, object]:
        """Return JSON schema for this rule type's configuration.

        Override in subclasses to provide schema for UI generation.

        Returns:
            JSON Schema dictionary describing valid configuration.
        """
        return {}


class RuleTypeRegistry:
    """Registry for pluggable rule types.

    Enables new rule types without schema changes by registering
    rule type classes that can evaluate configurations at runtime.

    Usage:
        # Register a new rule type
        @RuleTypeRegistry.register
        class MyRuleType(RuleType):
            name = "my_rule"
            ...

        # Look up a rule type
        rule_type = RuleTypeRegistry.get("my_rule")
        if rule_type:
            evaluator = rule_type()
            result = evaluator.evaluate(config, context)
    """

    _rule_types: ClassVar[dict[str, type[RuleType]]] = {}

    @classmethod
    def register(cls, rule_type: type[RuleType]) -> type[RuleType]:
        """Register a rule type. Can be used as a decorator.

        Args:
            rule_type: The RuleType subclass to register.

        Returns:
            The registered rule type (for decorator chaining).

        Raises:
            ValueError: If rule type has no name attribute.
        """
        if not hasattr(rule_type, "name") or not rule_type.name:
            raise ValueError(f"RuleType {rule_type.__name__} must define 'name'")
        cls._rule_types[rule_type.name] = rule_type
        return rule_type

    @classmethod
    def get(cls, name: str) -> type[RuleType] | None:
        """Retrieve a registered rule type by name.

        Args:
            name: The rule type name.

        Returns:
            The rule type class or None if not found.
        """
        return cls._rule_types.get(name)

    @classmethod
    def clear(cls) -> None:
        """Clear all registered rule types. For testing only."""
        cls._rule_types.clear()
