"""
Auto-Response Handler - Automated handling of CLI interaction prompts.

Provides:
- Automated response to low-risk prompts
- Escalation of high-risk prompts
- Policy-based response decisions
- Response history tracking
"""

import asyncio
import logging
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Callable, Optional

from .detector import (
    InteractionDetector,
    DetectedInteraction,
    InteractionType,
    RiskLevel,
    get_interaction_detector,
)
from ..subscriptions import SubscriptionManager, get_subscription_manager

logger = logging.getLogger(__name__)


class ResponseAction(Enum):
    """Actions the auto-handler can take."""

    AUTO_RESPOND = "auto_respond"  # Automatically send response
    ESCALATE = "escalate"  # Escalate to user/supervisor
    DEFER = "defer"  # Wait for more context
    REJECT = "reject"  # Reject/cancel the operation
    SKIP = "skip"  # Skip without response


class EscalationLevel(Enum):
    """Escalation levels for prompts."""

    NONE = "none"  # No escalation needed
    NOTIFY = "notify"  # Notify but continue
    REQUIRE_APPROVAL = "require_approval"  # Must get approval
    BLOCK = "block"  # Block operation entirely


@dataclass
class ResponseDecision:
    """Decision made by the auto-handler."""

    interaction: DetectedInteraction
    action: ResponseAction
    response_text: Optional[str]
    escalation_level: EscalationLevel
    reason: str
    decided_at: datetime = field(default_factory=datetime.now)
    metadata: dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "interaction": self.interaction.to_dict(),
            "action": self.action.value,
            "response_text": self.response_text,
            "escalation_level": self.escalation_level.value,
            "reason": self.reason,
            "decided_at": self.decided_at.isoformat(),
            "metadata": self.metadata,
        }


@dataclass
class ResponsePolicy:
    """Policy for auto-response decisions."""

    # Risk level thresholds
    auto_respond_max_risk: RiskLevel = RiskLevel.LOW
    escalate_min_risk: RiskLevel = RiskLevel.HIGH

    # Interaction type overrides
    always_auto_respond: list[InteractionType] = field(default_factory=list)
    never_auto_respond: list[InteractionType] = field(default_factory=list)
    always_escalate: list[InteractionType] = field(default_factory=list)

    # Rate limiting
    max_auto_responses_per_hour: int = 100
    cooldown_seconds: int = 1

    # Safety settings
    require_approval_for_destructive: bool = True
    block_dangerous_commands: bool = True

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "auto_respond_max_risk": self.auto_respond_max_risk.value,
            "escalate_min_risk": self.escalate_min_risk.value,
            "always_auto_respond": [t.value for t in self.always_auto_respond],
            "never_auto_respond": [t.value for t in self.never_auto_respond],
            "always_escalate": [t.value for t in self.always_escalate],
            "max_auto_responses_per_hour": self.max_auto_responses_per_hour,
            "cooldown_seconds": self.cooldown_seconds,
            "require_approval_for_destructive": self.require_approval_for_destructive,
            "block_dangerous_commands": self.block_dangerous_commands,
        }


class AutoResponseHandler:
    """
    Handle CLI interaction prompts automatically.

    Makes decisions about how to respond to detected interactions
    based on risk level, interaction type, and configured policies.

    Example:
        handler = AutoResponseHandler()

        # Set policy
        handler.set_policy(ResponsePolicy(
            auto_respond_max_risk=RiskLevel.MEDIUM,
        ))

        # Handle an interaction
        decision = handler.handle(interaction)
        if decision.action == ResponseAction.AUTO_RESPOND:
            send_response(decision.response_text)
    """

    def __init__(
        self,
        detector: Optional[InteractionDetector] = None,
        subscription_manager: Optional[SubscriptionManager] = None,
        policy: Optional[ResponsePolicy] = None,
    ) -> None:
        """
        Initialize the auto-response handler.

        Args:
            detector: Interaction detector instance
            subscription_manager: Subscription manager for checking agent configs
            policy: Response policy configuration
        """
        self._detector = detector or get_interaction_detector()
        self._subscription_manager = subscription_manager or get_subscription_manager()
        self._policy = policy or ResponsePolicy()
        self._response_history: list[ResponseDecision] = []
        self._callbacks: dict[ResponseAction, list[Callable[[ResponseDecision], None]]] = {
            action: [] for action in ResponseAction
        }
        self._escalation_callbacks: list[Callable[[ResponseDecision], None]] = []
        self._auto_response_count = 0
        self._last_response_reset = datetime.now()

    def set_policy(self, policy: ResponsePolicy) -> None:
        """Update the response policy."""
        self._policy = policy
        logger.info("Updated auto-response policy")

    def get_policy(self) -> ResponsePolicy:
        """Get current response policy."""
        return self._policy

    def handle(self, interaction: DetectedInteraction) -> ResponseDecision:
        """
        Handle a detected interaction and decide on response.

        Args:
            interaction: The detected interaction

        Returns:
            ResponseDecision with action and response
        """
        # Check agent subscription settings
        agent_config = self._subscription_manager.get_config(interaction.agent_id)
        if agent_config and not agent_config.auto_response_enabled:
            return self._create_decision(
                interaction,
                ResponseAction.ESCALATE,
                None,
                EscalationLevel.REQUIRE_APPROVAL,
                "Auto-response disabled for agent",
            )

        # Check rate limiting
        if not self._check_rate_limit():
            return self._create_decision(
                interaction,
                ResponseAction.DEFER,
                None,
                EscalationLevel.NOTIFY,
                "Auto-response rate limit exceeded",
            )

        # Determine action based on policy
        action, response, escalation, reason = self._evaluate_policy(interaction)

        decision = self._create_decision(
            interaction,
            action,
            response,
            escalation,
            reason,
        )

        # Track response
        self._response_history.append(decision)
        if action == ResponseAction.AUTO_RESPOND:
            self._auto_response_count += 1

        # Fire callbacks
        self._fire_callbacks(decision)

        return decision

    def _evaluate_policy(
        self,
        interaction: DetectedInteraction,
    ) -> tuple[ResponseAction, Optional[str], EscalationLevel, str]:
        """Evaluate policy to determine response action."""
        policy = self._policy
        int_type = interaction.interaction_type
        risk = interaction.risk_level

        # Check always_escalate first
        if int_type in policy.always_escalate:
            return (
                ResponseAction.ESCALATE,
                None,
                EscalationLevel.REQUIRE_APPROVAL,
                f"Interaction type {int_type.value} requires escalation",
            )

        # Check for dangerous commands
        if risk == RiskLevel.CRITICAL and policy.block_dangerous_commands:
            return (
                ResponseAction.REJECT,
                None,
                EscalationLevel.BLOCK,
                "Dangerous command blocked by policy",
            )

        # Check never_auto_respond
        if int_type in policy.never_auto_respond:
            return (
                ResponseAction.ESCALATE,
                None,
                EscalationLevel.REQUIRE_APPROVAL,
                f"Interaction type {int_type.value} cannot be auto-responded",
            )

        # Check always_auto_respond
        if int_type in policy.always_auto_respond:
            response = interaction.suggested_response
            if response is not None:
                return (
                    ResponseAction.AUTO_RESPOND,
                    response,
                    EscalationLevel.NONE,
                    f"Auto-respond policy for {int_type.value}",
                )

        # Check risk level
        if self._risk_allows_auto(risk, policy.auto_respond_max_risk):
            response = interaction.suggested_response
            if response is not None:
                return (
                    ResponseAction.AUTO_RESPOND,
                    response,
                    EscalationLevel.NONE,
                    f"Risk level {risk.value} allows auto-response",
                )

        # Check if escalation required
        if self._risk_requires_escalation(risk, policy.escalate_min_risk):
            return (
                ResponseAction.ESCALATE,
                None,
                EscalationLevel.REQUIRE_APPROVAL,
                f"Risk level {risk.value} requires escalation",
            )

        # Default: notify but don't auto-respond
        return (
            ResponseAction.DEFER,
            None,
            EscalationLevel.NOTIFY,
            "No auto-response rule matched",
        )

    def _risk_allows_auto(self, risk: RiskLevel, max_risk: RiskLevel) -> bool:
        """Check if risk level allows auto-response."""
        risk_order = [RiskLevel.LOW, RiskLevel.MEDIUM, RiskLevel.HIGH, RiskLevel.CRITICAL]
        return risk_order.index(risk) <= risk_order.index(max_risk)

    def _risk_requires_escalation(self, risk: RiskLevel, min_risk: RiskLevel) -> bool:
        """Check if risk level requires escalation."""
        risk_order = [RiskLevel.LOW, RiskLevel.MEDIUM, RiskLevel.HIGH, RiskLevel.CRITICAL]
        return risk_order.index(risk) >= risk_order.index(min_risk)

    def _check_rate_limit(self) -> bool:
        """Check if auto-response rate limit allows another response."""
        now = datetime.now()
        hours_elapsed = (now - self._last_response_reset).total_seconds() / 3600

        if hours_elapsed >= 1:
            self._auto_response_count = 0
            self._last_response_reset = now
            return True

        return self._auto_response_count < self._policy.max_auto_responses_per_hour

    def _create_decision(
        self,
        interaction: DetectedInteraction,
        action: ResponseAction,
        response: Optional[str],
        escalation: EscalationLevel,
        reason: str,
    ) -> ResponseDecision:
        """Create a response decision."""
        return ResponseDecision(
            interaction=interaction,
            action=action,
            response_text=response,
            escalation_level=escalation,
            reason=reason,
        )

    def on_action(
        self,
        action: ResponseAction,
        callback: Callable[[ResponseDecision], None],
    ) -> None:
        """Register callback for specific action type."""
        self._callbacks[action].append(callback)

    def on_escalation(
        self,
        callback: Callable[[ResponseDecision], None],
    ) -> None:
        """Register callback for escalations."""
        self._escalation_callbacks.append(callback)

    def _fire_callbacks(self, decision: ResponseDecision) -> None:
        """Fire registered callbacks."""
        # Fire action-specific callbacks
        for callback in self._callbacks.get(decision.action, []):
            try:
                callback(decision)
            except Exception as e:
                logger.error(f"Error in action callback: {e}")

        # Fire escalation callbacks if needed
        if decision.escalation_level != EscalationLevel.NONE:
            for callback in self._escalation_callbacks:
                try:
                    callback(decision)
                except Exception as e:
                    logger.error(f"Error in escalation callback: {e}")

    def get_history(
        self,
        agent_id: Optional[str] = None,
        action: Optional[ResponseAction] = None,
        limit: int = 100,
    ) -> list[ResponseDecision]:
        """
        Get response history.

        Args:
            agent_id: Filter by agent
            action: Filter by action type
            limit: Maximum results

        Returns:
            List of response decisions
        """
        results = self._response_history

        if agent_id:
            results = [d for d in results if d.interaction.agent_id == agent_id]

        if action:
            results = [d for d in results if d.action == action]

        return results[-limit:]

    def get_stats(self) -> dict[str, Any]:
        """Get handler statistics."""
        action_counts = {}
        escalation_counts = {}

        for decision in self._response_history:
            action_counts[decision.action.value] = action_counts.get(decision.action.value, 0) + 1
            escalation_counts[decision.escalation_level.value] = (
                escalation_counts.get(decision.escalation_level.value, 0) + 1
            )

        return {
            "total_decisions": len(self._response_history),
            "by_action": action_counts,
            "by_escalation": escalation_counts,
            "auto_responses_this_hour": self._auto_response_count,
            "policy": self._policy.to_dict(),
        }

    def clear_history(self) -> None:
        """Clear response history."""
        self._response_history.clear()


class InteractionRouter:
    """
    Route interactions to appropriate handlers.

    Coordinates between detection, auto-handling, and manual escalation.

    Example:
        router = InteractionRouter()

        # Register handlers
        router.on_auto_response(send_to_cli)
        router.on_escalation(notify_user)

        # Start routing
        await router.start()
    """

    def __init__(
        self,
        detector: Optional[InteractionDetector] = None,
        handler: Optional[AutoResponseHandler] = None,
    ) -> None:
        """Initialize the router."""
        self._detector = detector or get_interaction_detector()
        self._handler = handler or AutoResponseHandler(detector=self._detector)
        self._running = False
        self._auto_response_callback: Optional[Callable[[str, str], None]] = None
        self._escalation_callback: Optional[Callable[[ResponseDecision], None]] = None

    def on_auto_response(
        self,
        callback: Callable[[str, str], None],
    ) -> None:
        """
        Register callback for auto-responses.

        Callback receives (agent_id, response_text).
        """
        self._auto_response_callback = callback

    def on_escalation(
        self,
        callback: Callable[[ResponseDecision], None],
    ) -> None:
        """Register callback for escalations."""
        self._escalation_callback = callback

    async def start(self, check_interval: float = 2.0) -> None:
        """
        Start the interaction routing loop.

        Args:
            check_interval: Seconds between checks
        """
        self._running = True
        logger.info("Starting interaction router")

        while self._running:
            try:
                await self._process_interactions()
            except Exception as e:
                logger.error(f"Error in routing loop: {e}")

            await asyncio.sleep(check_interval)

    def stop(self) -> None:
        """Stop the routing loop."""
        self._running = False
        logger.info("Stopping interaction router")

    async def _process_interactions(self) -> None:
        """Process all pending interactions."""
        interactions = self._detector.check_all_agents()

        for interaction in interactions:
            decision = self._handler.handle(interaction)
            await self._route_decision(decision)

    async def _route_decision(self, decision: ResponseDecision) -> None:
        """Route a decision to appropriate handler."""
        if decision.action == ResponseAction.AUTO_RESPOND:
            if self._auto_response_callback and decision.response_text is not None:
                try:
                    self._auto_response_callback(
                        decision.interaction.agent_id,
                        decision.response_text,
                    )
                    logger.info(
                        f"Auto-responded to {decision.interaction.agent_id}: "
                        f"{decision.response_text!r}"
                    )
                except Exception as e:
                    logger.error(f"Error sending auto-response: {e}")

        elif decision.action == ResponseAction.ESCALATE:
            if self._escalation_callback:
                try:
                    self._escalation_callback(decision)
                    logger.info(
                        f"Escalated interaction from {decision.interaction.agent_id}: "
                        f"{decision.reason}"
                    )
                except Exception as e:
                    logger.error(f"Error in escalation callback: {e}")

    def process_single(self, agent_id: str) -> Optional[ResponseDecision]:
        """
        Process a single agent's interaction synchronously.

        Args:
            agent_id: Agent to check

        Returns:
            ResponseDecision if interaction found
        """
        interaction = self._detector.check_agent(agent_id)
        if interaction:
            return self._handler.handle(interaction)
        return None


# Module-level handler instance
_handler: Optional[AutoResponseHandler] = None
_router: Optional[InteractionRouter] = None


def get_auto_handler() -> AutoResponseHandler:
    """Get or create the global auto-response handler."""
    global _handler
    if _handler is None:
        _handler = AutoResponseHandler()
    return _handler


def set_auto_handler(handler: Optional[AutoResponseHandler]) -> None:
    """Set the global auto-response handler."""
    global _handler
    _handler = handler


def get_interaction_router() -> InteractionRouter:
    """Get or create the global interaction router."""
    global _router
    if _router is None:
        _router = InteractionRouter()
    return _router


def set_interaction_router(router: Optional[InteractionRouter]) -> None:
    """Set the global interaction router."""
    global _router
    _router = router
