from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Literal
from uuid import uuid4

from nexus.resilience.errors import NexusError


def _event_id() -> str:
    return str(uuid4())


def _ts() -> datetime:
    return datetime.now(timezone.utc)


@dataclass(frozen=True)
class BaseEvent:
    source_id: str
    event_id: str = field(default_factory=_event_id)
    timestamp: datetime = field(default_factory=_ts)


@dataclass(frozen=True)
class TaskStartEvent(BaseEvent):
    node_id: str = ""
    agent_id: str = ""
    task_input: dict[str, Any] = field(default_factory=dict)


@dataclass(frozen=True)
class TaskCompleteEvent(BaseEvent):
    node_id: str = ""
    agent_id: str = ""
    result: Any = None
    tokens_used: int = 0
    duration_ms: float = 0.0


@dataclass(frozen=True)
class TaskFailedEvent(BaseEvent):
    node_id: str = ""
    agent_id: str = ""
    error: NexusError | None = None
    attempt: int = 1


@dataclass(frozen=True)
class GraphMutationEvent(BaseEvent):
    mutation_type: Literal["add_node", "remove_node", "add_edge", "fork"] = "add_node"
    payload: dict[str, Any] = field(default_factory=dict)


@dataclass(frozen=True)
class StateMutationEvent(BaseEvent):
    layer: Literal["hot", "warm", "cold"] = "hot"
    key: str = ""
    old_value: Any = None
    new_value: Any = None


@dataclass(frozen=True)
class BudgetWarningEvent(BaseEvent):
    agent_id: str = ""
    used_tokens: int = 0
    allocated_tokens: int = 0
    pct: float = 0.0


@dataclass(frozen=True)
class BudgetExhaustedEvent(BaseEvent):
    agent_id: str = ""
    used_tokens: int = 0
    allocated_tokens: int = 0


@dataclass(frozen=True)
class ReflectionCompleteEvent(BaseEvent):
    agent_id: str = ""
    reflection: dict[str, Any] = field(default_factory=dict)
    quality_score: float = 0.0


@dataclass(frozen=True)
class SessionStartEvent(BaseEvent):
    session_id: str = ""
    config: dict[str, Any] = field(default_factory=dict)


@dataclass(frozen=True)
class SessionEndEvent(BaseEvent):
    session_id: str = ""
    total_tokens: int = 0
    duration_ms: float = 0.0
    success: bool = True


@dataclass(frozen=True)
class CircuitOpenEvent(BaseEvent):
    target_id: str = ""
    target_type: str = ""
    failure_count: int = 0


@dataclass(frozen=True)
class CircuitCloseEvent(BaseEvent):
    target_id: str = ""
    recovery_latency_ms: float = 0.0


@dataclass(frozen=True)
class CheckpointWrittenEvent(BaseEvent):
    checkpoint_id: str = ""
    path: str = ""
    state_size_bytes: int = 0


@dataclass(frozen=True)
class LLMCallEvent(BaseEvent):
    provider: str = ""
    model: str = ""
    input_tokens: int = 0
    output_tokens: int = 0
    latency_ms: float = 0.0
