"""
Behavior Plugin Architecture — Composable, Testable Bot Actions.

Design goals:
1. Each behavior is a self-contained plugin with a clear lifecycle
2. Plugins declare prerequisites (what screen state they require)
3. Plugins are registered in a priority-sorted registry
4. The feed loop queries the registry: "which plugins want to act on this post?"
5. Each plugin can be tested in complete isolation

This is the "neural pathway" system — instead of one monolithic brain (bot_flow.py),
the bot has specialized pathways that fire when their conditions are met.

Tesla analogy: Instead of one "drive" function, there are composable behaviors
(lane-keep, auto-park, summon) that activate when relevant.
"""

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


@dataclass
class BehaviorContext:
    """
    Shared context passed to every behavior plugin.
    Contains everything a behavior needs to make decisions and act.
    """

    device: Any  # Android device facade
    configs: Any  # User configuration
    session_state: Any  # Current session state
    cognitive_stack: Dict[str, Any]  # Cognitive engines (growth, resonance, etc.)
    shared_state: Dict[str, Any] = field(default_factory=dict)  # State shared between plugins
    context_xml: str = ""  # Current screen XML dump
    sleep_mod: float = 1.0  # Active Inference sleep multiplier
    post_data: Optional[Dict] = None  # Extracted post content
    username: str = ""  # Current target username (if applicable)


@dataclass
class BehaviorResult:
    """
    Result returned by a behavior plugin after execution.
    Used by the orchestrator to decide what happens next.
    """

    executed: bool = False  # Did the behavior actually do something?
    should_continue: bool = True  # Should the feed loop continue to next post?
    should_skip: bool = False  # Should we skip to the next post immediately?
    interactions: int = 0  # Number of interactions performed
    metadata: Dict[str, Any] = field(default_factory=dict)  # Plugin-specific data


class BehaviorPlugin(ABC):
    """
    Base class for all behavior plugins.

    Lifecycle:
    1. `can_activate(ctx)` — Should this behavior fire for this context?
    2. `priority` — If multiple behaviors can activate, higher priority goes first.
    3. `execute(ctx)` — Run the behavior.

    Rules:
    - Plugins must be stateless between posts (state lives in session_state)
    - Plugins must handle their own errors (never crash the feed loop)
    - Plugins must respect session limits via ctx.session_state
    """

    @property
    @abstractmethod
    def name(self) -> str:
        """Unique identifier for this behavior."""
        ...

    @property
    def priority(self) -> int:
        """
        Execution priority. Higher = runs first.

        Guidelines:
        - 100+: Safety/guard behaviors (ad detection, block detection)
        - 50-99: Primary interactions (like, follow, comment)
        - 10-49: Secondary interactions (carousel, story view)
        - 1-9:   Observational behaviors (scraping, analytics)
        """
        return 50

    @property
    def exclusive(self) -> bool:
        """
        If True, no other behavior can run after this one on the same post.
        Used for guard behaviors that abort interaction (e.g., ad detection).
        """
        return False

    @abstractmethod
    def can_activate(self, ctx: BehaviorContext) -> bool:
        """
        Returns True if this behavior should fire for the given context.
        Must be cheap to evaluate (no device interactions).
        """
        ...

    @abstractmethod
    def execute(self, ctx: BehaviorContext) -> BehaviorResult:
        """
        Execute the behavior. Must handle all errors internally.
        Returns a BehaviorResult describing what happened.
        """
        ...

    def get_config(self, ctx: BehaviorContext) -> dict:
        """Helper to retrieve plugin-specific configuration."""
        return ctx.configs.get_plugin_config(self.name)

    def __repr__(self):
        return f"<{self.__class__.__name__} name={self.name} priority={self.priority}>"


class PluginRegistry:
    """
    Central registry for behavior plugins.

    Manages plugin registration, priority sorting, and orchestrated execution.
    Thread-safe singleton.
    """

    _instance = None

    @classmethod
    def get_instance(cls) -> "PluginRegistry":
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    @classmethod
    def reset(cls):
        """Wipe the registry singleton instance."""
        cls._instance = None

    def __init__(self):
        self._plugins: List[BehaviorPlugin] = []
        self._sorted = False

    def register(self, plugin: BehaviorPlugin):
        """Register a behavior plugin."""
        # Prevent duplicate registration
        for existing in self._plugins:
            if existing.name == plugin.name:
                logger.debug(f"Plugin '{plugin.name}' already registered. Skipping.")
                return

        self._plugins.append(plugin)
        self._sorted = False
        logger.info(f"🧩 [Plugin] Registered: {plugin.name} (priority={plugin.priority})")

    def unregister(self, name: str):
        """Remove a plugin by name."""
        self._plugins = [p for p in self._plugins if p.name != name]
        self._sorted = False

    def _ensure_sorted(self):
        """Sort plugins by priority (highest first)."""
        if not self._sorted:
            self._plugins.sort(key=lambda p: p.priority, reverse=True)
            self._sorted = True

    @property
    def plugins(self) -> List[BehaviorPlugin]:
        """Returns all plugins, sorted by priority."""
        self._ensure_sorted()
        return list(self._plugins)

    def get_active_plugins(self, ctx: BehaviorContext) -> List[BehaviorPlugin]:
        """Returns plugins that can activate for the given context, sorted by priority."""
        self._ensure_sorted()
        active = []
        for plugin in self._plugins:
            try:
                if plugin.can_activate(ctx):
                    active.append(plugin)
            except Exception as e:
                logger.error(f"🧩 [Plugin] Error checking {plugin.name}.can_activate: {e}")
        return active

    def execute_all(self, ctx: BehaviorContext) -> List[BehaviorResult]:
        """
        Execute all active plugins in priority order.

        Stops early if an exclusive plugin fires (e.g., ad guard).
        Returns list of results from all executed plugins.
        """
        self._ensure_sorted()
        results = []

        for plugin in self._plugins:
            try:
                if not plugin.can_activate(ctx):
                    continue

                logger.debug(f"🧩 [PluginRegistry] TRACE: Calling execute() on {plugin.name}")
                result = plugin.execute(ctx)
                results.append(result)

                if (plugin.exclusive and result.executed) or result.should_skip:
                    logger.debug(
                        f"🧩 [Plugin] {plugin.name} triggered chain termination (exclusive={plugin.exclusive}, should_skip={result.should_skip})."
                    )
                    break

            except Exception as e:
                logger.error(f"🧩 [Plugin] Error executing {plugin.name}: {e}")
                results.append(BehaviorResult(executed=False, metadata={"error": str(e)}))

        return results

    def __len__(self):
        return len(self._plugins)

    def __contains__(self, name: str):
        return any(p.name == name for p in self._plugins)


# Import plugins at the bottom to avoid circular imports
from GramAddict.core.behaviors.ad_guard import AdGuardPlugin  # noqa: E402
from GramAddict.core.behaviors.anomaly_handler import AnomalyHandlerPlugin  # noqa: E402
from GramAddict.core.behaviors.close_friends_guard import CloseFriendsGuardPlugin  # noqa: E402
from GramAddict.core.behaviors.comment import CommentPlugin  # noqa: E402
from GramAddict.core.behaviors.darwin_dwell import DarwinDwellPlugin  # noqa: E402
from GramAddict.core.behaviors.like import LikePlugin  # noqa: E402
from GramAddict.core.behaviors.obstacle_guard import ObstacleGuardPlugin  # noqa: E402
from GramAddict.core.behaviors.perfect_snapping import PerfectSnappingPlugin  # noqa: E402
from GramAddict.core.behaviors.post_data_extraction import PostDataExtractionPlugin  # noqa: E402
from GramAddict.core.behaviors.post_interaction import PostInteractionPlugin  # noqa: E402
from GramAddict.core.behaviors.profile_visit import ProfileVisitPlugin  # noqa: E402
from GramAddict.core.behaviors.rabbit_hole import RabbitHolePlugin  # noqa: E402
from GramAddict.core.behaviors.repost import RepostPlugin  # noqa: E402
from GramAddict.core.behaviors.resonance_evaluator import ResonanceEvaluatorPlugin  # noqa: E402
from GramAddict.core.behaviors.scrape_profile import ScrapeProfilePlugin  # noqa: E402

# Note: We do not automatically instantiate all of them globally here to avoid circular
# dependencies during initial load. The bot_flow.py engine should explicitly register them.


def load_all_plugins():
    """
    Registers all available core behavior plugins into the global registry.
    Useful for testing or full-agent initialization.
    """
    registry = PluginRegistry.get_instance()
    registry.register(AdGuardPlugin())
    registry.register(AnomalyHandlerPlugin())
    registry.register(CloseFriendsGuardPlugin())
    registry.register(CommentPlugin())
    registry.register(DarwinDwellPlugin())
    registry.register(LikePlugin())
    registry.register(ObstacleGuardPlugin())
    registry.register(PerfectSnappingPlugin())
    registry.register(PostDataExtractionPlugin())
    registry.register(PostInteractionPlugin())
    registry.register(ProfileVisitPlugin())
    registry.register(RabbitHolePlugin())
    registry.register(RepostPlugin())
    registry.register(ResonanceEvaluatorPlugin())
    registry.register(ScrapeProfilePlugin())
