"""
Human Handoff Service

Manages seamless transfer of conversations from AI to human agents.

Features:
- Trigger-based escalation (sentiment, keywords, user request)
- Agent queue management
- Context preservation
- Agent availability tracking
"""
from typing import Any, Dict, List, Optional
from enum import Enum
from datetime import datetime
from pydantic import BaseModel
import logging

logger = logging.getLogger(__name__)


class HandoffReason(str, Enum):
    """Reasons for handoff to human"""
    USER_REQUEST = "user_request"           # User asked for human
    NEGATIVE_SENTIMENT = "negative_sentiment"  # Detected frustration
    COMPLEX_QUERY = "complex_query"         # AI can't handle
    HIGH_VALUE = "high_value"               # High-value transaction
    SENSITIVE_TOPIC = "sensitive_topic"     # Complaint, legal, etc.
    REPEATED_FAILURE = "repeated_failure"   # AI failed multiple times
    CUSTOM_TRIGGER = "custom_trigger"       # Client-defined trigger


class HandoffStatus(str, Enum):
    """Status of handoff request"""
    PENDING = "pending"           # Waiting for agent
    ASSIGNED = "assigned"         # Agent assigned
    IN_PROGRESS = "in_progress"   # Agent handling
    RESOLVED = "resolved"         # Completed by agent
    RETURNED_TO_AI = "returned"   # Back to AI
    ABANDONED = "abandoned"       # User left
    TIMEOUT = "timeout"           # No agent available


class AgentStatus(str, Enum):
    """Human agent availability"""
    ONLINE = "online"
    BUSY = "busy"
    AWAY = "away"
    OFFLINE = "offline"


class HandoffRequest(BaseModel):
    """Handoff request model"""
    id: str
    conversation_id: str
    client_id: int
    user_id: Optional[str] = None
    reason: HandoffReason
    trigger_message: str
    context_summary: str
    priority: int = 5  # 1-10, higher = more urgent
    status: HandoffStatus = HandoffStatus.PENDING
    assigned_agent_id: Optional[str] = None
    created_at: datetime = datetime.utcnow()
    assigned_at: Optional[datetime] = None
    resolved_at: Optional[datetime] = None
    metadata: Dict[str, Any] = {}


class HumanAgent(BaseModel):
    """Human agent model"""
    id: str
    client_id: int
    name: str
    email: str
    status: AgentStatus = AgentStatus.OFFLINE
    current_conversations: int = 0
    max_conversations: int = 5
    skills: List[str] = []
    languages: List[str] = ["en"]


class HandoffConfig(BaseModel):
    """Client-specific handoff configuration"""
    enabled: bool = True

    # Trigger keywords
    handoff_keywords: List[str] = [
        "speak to human",
        "talk to agent",
        "real person",
        "customer service",
        "representative",
        "manager",
    ]

    # Sentiment threshold (0-1, lower = more negative)
    negative_sentiment_threshold: float = 0.3

    # Auto-escalate after N AI failures
    max_ai_failures: int = 3

    # High-value threshold (currency)
    high_value_threshold: float = 100000

    # Business hours for human availability
    business_hours: Dict[str, Any] = {
        "timezone": "Asia/Kolkata",
        "start": "09:00",
        "end": "18:00",
        "days": [0, 1, 2, 3, 4, 5]  # Mon-Sat
    }

    # Queue settings
    max_queue_size: int = 50
    queue_timeout_minutes: int = 30

    # Messages
    handoff_message: str = "I'm connecting you with a customer service representative. Please wait..."
    no_agent_message: str = "Our team is currently unavailable. Please leave your contact and we'll get back to you."
    queue_position_message: str = "You are #{position} in queue. Estimated wait: {wait_time} minutes."


class HandoffService:
    """
    Manages human handoff workflow.

    Flow:
    1. Detect handoff trigger (keyword, sentiment, etc.)
    2. Create handoff request
    3. Find available agent
    4. Transfer conversation with context
    5. Agent handles conversation
    6. Optionally return to AI
    """

    def __init__(self):
        # In-memory storage (replace with database in production)
        self._requests: Dict[str, HandoffRequest] = {}
        self._agents: Dict[str, HumanAgent] = {}
        self._configs: Dict[int, HandoffConfig] = {}
        self._queues: Dict[int, List[str]] = {}  # client_id -> request_ids

    # ==================== DETECTION ====================

    def should_handoff(
        self,
        message: str,
        client_id: int,
        sentiment_score: Optional[float] = None,
        ai_failure_count: int = 0,
        transaction_value: Optional[float] = None,
        context: Dict[str, Any] = None
    ) -> tuple[bool, Optional[HandoffReason]]:
        """
        Check if conversation should be handed off to human.

        Returns:
            (should_handoff, reason)
        """
        config = self._get_config(client_id)

        if not config.enabled:
            return False, None

        message_lower = message.lower()

        # Check for explicit handoff request
        for keyword in config.handoff_keywords:
            if keyword.lower() in message_lower:
                return True, HandoffReason.USER_REQUEST

        # Check sentiment
        if sentiment_score is not None and sentiment_score < config.negative_sentiment_threshold:
            return True, HandoffReason.NEGATIVE_SENTIMENT

        # Check AI failure count
        if ai_failure_count >= config.max_ai_failures:
            return True, HandoffReason.REPEATED_FAILURE

        # Check transaction value
        if transaction_value and transaction_value >= config.high_value_threshold:
            return True, HandoffReason.HIGH_VALUE

        # Check custom triggers from context
        if context and context.get("force_handoff"):
            return True, HandoffReason.CUSTOM_TRIGGER

        return False, None

    # ==================== REQUEST MANAGEMENT ====================

    async def create_handoff_request(
        self,
        conversation_id: str,
        client_id: int,
        reason: HandoffReason,
        trigger_message: str,
        conversation_history: List[Dict[str, Any]],
        user_info: Dict[str, Any] = None,
        metadata: Dict[str, Any] = None
    ) -> HandoffRequest:
        """Create a new handoff request"""
        import uuid

        # Generate context summary from conversation
        context_summary = self._summarize_conversation(conversation_history)

        # Calculate priority
        priority = self._calculate_priority(reason, metadata)

        request = HandoffRequest(
            id=str(uuid.uuid4()),
            conversation_id=conversation_id,
            client_id=client_id,
            user_id=user_info.get("id") if user_info else None,
            reason=reason,
            trigger_message=trigger_message,
            context_summary=context_summary,
            priority=priority,
            metadata=metadata or {}
        )

        self._requests[request.id] = request

        # Add to queue
        if client_id not in self._queues:
            self._queues[client_id] = []
        self._queues[client_id].append(request.id)

        logger.info(f"Handoff request created: {request.id} for conversation {conversation_id}")

        # Try to assign immediately
        await self._try_assign_agent(request)

        return request

    async def _try_assign_agent(self, request: HandoffRequest) -> bool:
        """Try to assign an available agent to the request"""
        available_agents = self._get_available_agents(request.client_id)

        if not available_agents:
            logger.info(f"No agents available for request {request.id}")
            return False

        # Sort by current workload
        available_agents.sort(key=lambda a: a.current_conversations)

        # Assign to least busy agent
        agent = available_agents[0]

        request.status = HandoffStatus.ASSIGNED
        request.assigned_agent_id = agent.id
        request.assigned_at = datetime.utcnow()

        agent.current_conversations += 1
        if agent.current_conversations >= agent.max_conversations:
            agent.status = AgentStatus.BUSY

        logger.info(f"Request {request.id} assigned to agent {agent.id}")

        # Remove from queue
        if request.client_id in self._queues:
            self._queues[request.client_id].remove(request.id)

        return True

    def _get_available_agents(self, client_id: int) -> List[HumanAgent]:
        """Get available agents for a client"""
        return [
            agent for agent in self._agents.values()
            if agent.client_id == client_id
            and agent.status == AgentStatus.ONLINE
            and agent.current_conversations < agent.max_conversations
        ]

    # ==================== AGENT ACTIONS ====================

    async def agent_accept(self, request_id: str, agent_id: str) -> Dict[str, Any]:
        """Agent accepts and starts handling the conversation"""
        request = self._requests.get(request_id)
        if not request:
            return {"success": False, "error": "Request not found"}

        if request.assigned_agent_id != agent_id:
            return {"success": False, "error": "Not assigned to this agent"}

        request.status = HandoffStatus.IN_PROGRESS

        return {
            "success": True,
            "conversation_id": request.conversation_id,
            "context_summary": request.context_summary,
            "reason": request.reason.value,
            "trigger_message": request.trigger_message
        }

    async def agent_resolve(
        self,
        request_id: str,
        agent_id: str,
        resolution_notes: str = None,
        return_to_ai: bool = False
    ) -> Dict[str, Any]:
        """Agent marks conversation as resolved"""
        request = self._requests.get(request_id)
        if not request:
            return {"success": False, "error": "Request not found"}

        request.status = HandoffStatus.RETURNED_TO_AI if return_to_ai else HandoffStatus.RESOLVED
        request.resolved_at = datetime.utcnow()

        if resolution_notes:
            request.metadata["resolution_notes"] = resolution_notes

        # Update agent workload
        agent = self._agents.get(agent_id)
        if agent:
            agent.current_conversations = max(0, agent.current_conversations - 1)
            if agent.current_conversations < agent.max_conversations:
                agent.status = AgentStatus.ONLINE

        logger.info(f"Request {request_id} resolved by agent {agent_id}")

        return {"success": True, "return_to_ai": return_to_ai}

    # ==================== QUEUE MANAGEMENT ====================

    def get_queue_position(self, request_id: str) -> Dict[str, Any]:
        """Get queue position for a request"""
        request = self._requests.get(request_id)
        if not request:
            return {"error": "Request not found"}

        if request.status != HandoffStatus.PENDING:
            return {"position": 0, "status": request.status.value}

        queue = self._queues.get(request.client_id, [])
        try:
            position = queue.index(request_id) + 1
        except ValueError:
            position = 0

        # Estimate wait time (2 minutes per position)
        estimated_wait = position * 2

        return {
            "position": position,
            "estimated_wait_minutes": estimated_wait,
            "status": request.status.value
        }

    def get_client_queue(self, client_id: int) -> List[Dict[str, Any]]:
        """Get all pending requests for a client"""
        queue_ids = self._queues.get(client_id, [])
        return [
            {
                "id": rid,
                "conversation_id": self._requests[rid].conversation_id,
                "reason": self._requests[rid].reason.value,
                "priority": self._requests[rid].priority,
                "wait_time_minutes": (datetime.utcnow() - self._requests[rid].created_at).seconds // 60
            }
            for rid in queue_ids
            if rid in self._requests
        ]

    # ==================== AGENT MANAGEMENT ====================

    def register_agent(self, agent: HumanAgent) -> Dict[str, Any]:
        """Register a new human agent"""
        self._agents[agent.id] = agent
        return {"success": True, "agent_id": agent.id}

    def update_agent_status(self, agent_id: str, status: AgentStatus) -> Dict[str, Any]:
        """Update agent availability status"""
        agent = self._agents.get(agent_id)
        if not agent:
            return {"success": False, "error": "Agent not found"}

        agent.status = status
        logger.info(f"Agent {agent_id} status updated to {status.value}")

        return {"success": True, "status": status.value}

    def get_agent_dashboard(self, agent_id: str) -> Dict[str, Any]:
        """Get agent's dashboard data"""
        agent = self._agents.get(agent_id)
        if not agent:
            return {"error": "Agent not found"}

        # Get assigned requests
        assigned = [
            r for r in self._requests.values()
            if r.assigned_agent_id == agent_id
            and r.status in [HandoffStatus.ASSIGNED, HandoffStatus.IN_PROGRESS]
        ]

        return {
            "agent": {
                "id": agent.id,
                "name": agent.name,
                "status": agent.status.value,
                "current_conversations": agent.current_conversations,
                "max_conversations": agent.max_conversations
            },
            "active_conversations": [
                {
                    "id": r.id,
                    "conversation_id": r.conversation_id,
                    "reason": r.reason.value,
                    "status": r.status.value,
                    "context_summary": r.context_summary,
                    "wait_time": (datetime.utcnow() - r.created_at).seconds // 60
                }
                for r in assigned
            ],
            "queue_size": len(self._queues.get(agent.client_id, []))
        }

    # ==================== HELPERS ====================

    def _get_config(self, client_id: int) -> HandoffConfig:
        """Get handoff config for client"""
        if client_id not in self._configs:
            self._configs[client_id] = HandoffConfig()
        return self._configs[client_id]

    def set_config(self, client_id: int, config: HandoffConfig):
        """Set handoff config for client"""
        self._configs[client_id] = config

    def _summarize_conversation(self, history: List[Dict[str, Any]]) -> str:
        """Create a summary of conversation for agent context"""
        if not history:
            return "No conversation history available."

        # Get last 5 messages
        recent = history[-5:]

        summary_parts = []
        for msg in recent:
            role = "User" if msg.get("role") == "user" else "AI"
            content = msg.get("content", "")[:200]  # Truncate long messages
            summary_parts.append(f"{role}: {content}")

        return "\n".join(summary_parts)

    def _calculate_priority(self, reason: HandoffReason, metadata: Dict = None) -> int:
        """Calculate priority score (1-10)"""
        base_priority = {
            HandoffReason.USER_REQUEST: 5,
            HandoffReason.NEGATIVE_SENTIMENT: 7,
            HandoffReason.COMPLEX_QUERY: 4,
            HandoffReason.HIGH_VALUE: 8,
            HandoffReason.SENSITIVE_TOPIC: 9,
            HandoffReason.REPEATED_FAILURE: 6,
            HandoffReason.CUSTOM_TRIGGER: 5,
        }

        priority = base_priority.get(reason, 5)

        # Adjust based on metadata
        if metadata:
            if metadata.get("vip_customer"):
                priority = min(10, priority + 2)
            if metadata.get("urgent"):
                priority = min(10, priority + 1)

        return priority


# Singleton instance
handoff_service = HandoffService()
