"""
State Models - Data classes for CLI agent state tracking.

Provides standardized models for:
- Rate limit states
- Session states
- Complete state snapshots
"""

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


class WindowType(Enum):
    """Rate limit window types."""

    FIVE_HOUR = "5_hour"
    THREE_HOUR = "3_hour"
    DAILY = "daily"
    WEEKLY = "weekly"
    UNLIMITED = "unlimited"


class WaitingState(Enum):
    """States for when CLI is waiting for input."""

    IDLE = "idle"  # Not waiting
    APPROVAL = "approval"  # Waiting for action approval
    CLARIFICATION = "clarification"  # Waiting for clarification
    TOOL_USE = "tool_use"  # Waiting for tool authorization
    CONTINUE = "continue"  # Waiting for continue/proceed
    CUSTOM = "custom"  # Custom prompt


@dataclass
class RateLimitState:
    """Current rate limit state for a CLI agent."""

    requests_used: int
    requests_limit: int
    reset_at: datetime
    window_type: WindowType
    percentage_used: float = field(init=False)

    def __post_init__(self):
        if self.requests_limit > 0:
            self.percentage_used = (self.requests_used / self.requests_limit) * 100
        else:
            self.percentage_used = 0.0

    @property
    def is_exhausted(self) -> bool:
        """Check if rate limit is exhausted."""
        return self.requests_used >= self.requests_limit

    @property
    def remaining(self) -> int:
        """Get remaining requests in window."""
        return max(0, self.requests_limit - self.requests_used)

    @property
    def time_until_reset(self) -> float:
        """Get seconds until reset."""
        delta = self.reset_at - datetime.now()
        return max(0.0, delta.total_seconds())

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "requests_used": self.requests_used,
            "requests_limit": self.requests_limit,
            "remaining": self.remaining,
            "percentage_used": round(self.percentage_used, 2),
            "is_exhausted": self.is_exhausted,
            "reset_at": self.reset_at.isoformat(),
            "time_until_reset_seconds": self.time_until_reset,
            "window_type": self.window_type.value,
        }


@dataclass
class SessionState:
    """Current session state for a CLI agent."""

    session_id: str
    started_at: datetime
    last_activity_at: datetime
    is_active: bool
    waiting_for_input: bool
    waiting_state: WaitingState = WaitingState.IDLE
    input_prompt: Optional[str] = None
    current_task: Optional[str] = None
    conversation_turns: int = 0
    total_tokens_used: int = 0
    metadata: dict[str, Any] = field(default_factory=dict)

    @property
    def duration_seconds(self) -> float:
        """Get session duration in seconds."""
        return (self.last_activity_at - self.started_at).total_seconds()

    @property
    def idle_seconds(self) -> float:
        """Get seconds since last activity."""
        return (datetime.now() - self.last_activity_at).total_seconds()

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "session_id": self.session_id,
            "started_at": self.started_at.isoformat(),
            "last_activity_at": self.last_activity_at.isoformat(),
            "duration_seconds": self.duration_seconds,
            "idle_seconds": self.idle_seconds,
            "is_active": self.is_active,
            "waiting_for_input": self.waiting_for_input,
            "waiting_state": self.waiting_state.value,
            "input_prompt": self.input_prompt,
            "current_task": self.current_task,
            "conversation_turns": self.conversation_turns,
            "total_tokens_used": self.total_tokens_used,
        }


@dataclass
class CLIStateSnapshot:
    """Complete state snapshot for a CLI agent."""

    agent_type: str  # "claude", "codex", "gemini"
    agent_id: str
    captured_at: datetime = field(default_factory=datetime.now)
    rate_limit: Optional[RateLimitState] = None
    session: Optional[SessionState] = None
    is_installed: bool = False
    state_dir_exists: bool = False
    last_error: Optional[str] = None

    @property
    def is_available(self) -> bool:
        """Check if agent is available for new tasks."""
        if not self.is_installed or not self.state_dir_exists:
            return False
        if self.rate_limit and self.rate_limit.is_exhausted:
            return False
        if self.session and self.session.waiting_for_input:
            return False
        return True

    @property
    def availability_reason(self) -> str:
        """Get reason for availability status."""
        if not self.is_installed:
            return "CLI not installed"
        if not self.state_dir_exists:
            return "State directory not found"
        if self.rate_limit and self.rate_limit.is_exhausted:
            return f"Rate limit exhausted (resets at {self.rate_limit.reset_at})"
        if self.session and self.session.waiting_for_input:
            return f"Waiting for input: {self.session.waiting_state.value}"
        return "Available"

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "agent_type": self.agent_type,
            "agent_id": self.agent_id,
            "captured_at": self.captured_at.isoformat(),
            "is_available": self.is_available,
            "availability_reason": self.availability_reason,
            "is_installed": self.is_installed,
            "state_dir_exists": self.state_dir_exists,
            "rate_limit": self.rate_limit.to_dict() if self.rate_limit else None,
            "session": self.session.to_dict() if self.session else None,
            "last_error": self.last_error,
        }


@dataclass
class AgentHealthStatus:
    """Overall health status for an agent."""

    agent_id: str
    agent_type: str
    status: str  # "healthy", "warning", "critical", "unavailable"
    rate_limit_percentage: float
    is_rate_limited: bool
    is_waiting_for_input: bool
    last_activity_seconds_ago: float
    message: str

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "agent_id": self.agent_id,
            "agent_type": self.agent_type,
            "status": self.status,
            "rate_limit_percentage": round(self.rate_limit_percentage, 2),
            "is_rate_limited": self.is_rate_limited,
            "is_waiting_for_input": self.is_waiting_for_input,
            "last_activity_seconds_ago": round(self.last_activity_seconds_ago, 1),
            "message": self.message,
        }
