"""
Event schemas for the Syndarix EventBus (Redis Pub/Sub).

This module defines event types and payload schemas for real-time communication
between services, agents, and the frontend.
"""

from datetime import datetime
from enum import Enum
from typing import Literal
from uuid import UUID

from pydantic import BaseModel, Field


class EventType(str, Enum):
    """
    Event types for the EventBus.

    Naming convention: {domain}.{action}
    """

    # Agent Events
    AGENT_SPAWNED = "agent.spawned"
    AGENT_STATUS_CHANGED = "agent.status_changed"
    AGENT_MESSAGE = "agent.message"
    AGENT_TERMINATED = "agent.terminated"

    # Issue Events
    ISSUE_CREATED = "issue.created"
    ISSUE_UPDATED = "issue.updated"
    ISSUE_ASSIGNED = "issue.assigned"
    ISSUE_CLOSED = "issue.closed"

    # Sprint Events
    SPRINT_STARTED = "sprint.started"
    SPRINT_COMPLETED = "sprint.completed"

    # Approval Events
    APPROVAL_REQUESTED = "approval.requested"
    APPROVAL_GRANTED = "approval.granted"
    APPROVAL_DENIED = "approval.denied"

    # Project Events
    PROJECT_CREATED = "project.created"
    PROJECT_UPDATED = "project.updated"
    PROJECT_ARCHIVED = "project.archived"

    # Workflow Events
    WORKFLOW_STARTED = "workflow.started"
    WORKFLOW_STEP_COMPLETED = "workflow.step_completed"
    WORKFLOW_COMPLETED = "workflow.completed"
    WORKFLOW_FAILED = "workflow.failed"


ActorType = Literal["agent", "user", "system"]


class Event(BaseModel):
    """
    Base event schema for the EventBus.

    All events published to the EventBus must conform to this schema.
    """

    id: str = Field(
        ...,
        description="Unique event identifier (UUID string)",
        examples=["550e8400-e29b-41d4-a716-446655440000"],
    )
    type: EventType = Field(
        ...,
        description="Event type enum value",
        examples=[EventType.AGENT_MESSAGE],
    )
    timestamp: datetime = Field(
        ...,
        description="When the event occurred (UTC)",
        examples=["2024-01-15T10:30:00Z"],
    )
    project_id: UUID = Field(
        ...,
        description="Project this event belongs to",
        examples=["550e8400-e29b-41d4-a716-446655440001"],
    )
    actor_id: UUID | None = Field(
        default=None,
        description="ID of the agent or user who triggered the event",
        examples=["550e8400-e29b-41d4-a716-446655440002"],
    )
    actor_type: ActorType = Field(
        ...,
        description="Type of actor: 'agent', 'user', or 'system'",
        examples=["agent"],
    )
    payload: dict = Field(
        default_factory=dict,
        description="Event-specific payload data",
    )

    model_config = {
        "json_schema_extra": {
            "example": {
                "id": "550e8400-e29b-41d4-a716-446655440000",
                "type": "agent.message",
                "timestamp": "2024-01-15T10:30:00Z",
                "project_id": "550e8400-e29b-41d4-a716-446655440001",
                "actor_id": "550e8400-e29b-41d4-a716-446655440002",
                "actor_type": "agent",
                "payload": {"message": "Processing task...", "progress": 50},
            }
        }
    }


# Specific payload schemas for type safety


class AgentSpawnedPayload(BaseModel):
    """Payload for AGENT_SPAWNED events."""

    agent_instance_id: UUID = Field(..., description="ID of the spawned agent instance")
    agent_type_id: UUID = Field(..., description="ID of the agent type")
    agent_name: str = Field(..., description="Human-readable name of the agent")
    role: str = Field(..., description="Agent role (e.g., 'product_owner', 'engineer')")


class AgentStatusChangedPayload(BaseModel):
    """Payload for AGENT_STATUS_CHANGED events."""

    agent_instance_id: UUID = Field(..., description="ID of the agent instance")
    previous_status: str = Field(..., description="Previous status")
    new_status: str = Field(..., description="New status")
    reason: str | None = Field(default=None, description="Reason for status change")


class AgentMessagePayload(BaseModel):
    """Payload for AGENT_MESSAGE events."""

    agent_instance_id: UUID = Field(..., description="ID of the agent instance")
    message: str = Field(..., description="Message content")
    message_type: str = Field(
        default="info",
        description="Message type: 'info', 'warning', 'error', 'debug'",
    )
    metadata: dict = Field(
        default_factory=dict,
        description="Additional metadata (e.g., token usage, model info)",
    )


class AgentTerminatedPayload(BaseModel):
    """Payload for AGENT_TERMINATED events."""

    agent_instance_id: UUID = Field(..., description="ID of the agent instance")
    termination_reason: str = Field(..., description="Reason for termination")
    final_status: str = Field(..., description="Final status at termination")


class IssueCreatedPayload(BaseModel):
    """Payload for ISSUE_CREATED events."""

    issue_id: str = Field(..., description="Issue ID (from external tracker)")
    title: str = Field(..., description="Issue title")
    priority: str | None = Field(default=None, description="Issue priority")
    labels: list[str] = Field(default_factory=list, description="Issue labels")


class IssueUpdatedPayload(BaseModel):
    """Payload for ISSUE_UPDATED events."""

    issue_id: str = Field(..., description="Issue ID (from external tracker)")
    changes: dict = Field(..., description="Dictionary of field changes")


class IssueAssignedPayload(BaseModel):
    """Payload for ISSUE_ASSIGNED events."""

    issue_id: str = Field(..., description="Issue ID (from external tracker)")
    assignee_id: UUID | None = Field(
        default=None, description="Agent or user assigned to"
    )
    assignee_name: str | None = Field(default=None, description="Assignee name")


class IssueClosedPayload(BaseModel):
    """Payload for ISSUE_CLOSED events."""

    issue_id: str = Field(..., description="Issue ID (from external tracker)")
    resolution: str = Field(..., description="Resolution status")


class SprintStartedPayload(BaseModel):
    """Payload for SPRINT_STARTED events."""

    sprint_id: UUID = Field(..., description="Sprint ID")
    sprint_name: str = Field(..., description="Sprint name")
    goal: str | None = Field(default=None, description="Sprint goal")
    issue_count: int = Field(default=0, description="Number of issues in sprint")


class SprintCompletedPayload(BaseModel):
    """Payload for SPRINT_COMPLETED events."""

    sprint_id: UUID = Field(..., description="Sprint ID")
    sprint_name: str = Field(..., description="Sprint name")
    completed_issues: int = Field(default=0, description="Number of completed issues")
    incomplete_issues: int = Field(
        default=0, description="Number of incomplete issues"
    )


class ApprovalRequestedPayload(BaseModel):
    """Payload for APPROVAL_REQUESTED events."""

    approval_id: UUID = Field(..., description="Approval request ID")
    approval_type: str = Field(..., description="Type of approval needed")
    description: str = Field(..., description="Description of what needs approval")
    requested_by: UUID | None = Field(
        default=None, description="Agent/user requesting approval"
    )
    timeout_minutes: int | None = Field(
        default=None, description="Minutes before auto-escalation"
    )


class ApprovalGrantedPayload(BaseModel):
    """Payload for APPROVAL_GRANTED events."""

    approval_id: UUID = Field(..., description="Approval request ID")
    approved_by: UUID = Field(..., description="User who granted approval")
    comments: str | None = Field(default=None, description="Approval comments")


class ApprovalDeniedPayload(BaseModel):
    """Payload for APPROVAL_DENIED events."""

    approval_id: UUID = Field(..., description="Approval request ID")
    denied_by: UUID = Field(..., description="User who denied approval")
    reason: str = Field(..., description="Reason for denial")


class WorkflowStartedPayload(BaseModel):
    """Payload for WORKFLOW_STARTED events."""

    workflow_id: UUID = Field(..., description="Workflow execution ID")
    workflow_type: str = Field(..., description="Type of workflow")
    total_steps: int = Field(default=0, description="Total number of steps")


class WorkflowStepCompletedPayload(BaseModel):
    """Payload for WORKFLOW_STEP_COMPLETED events."""

    workflow_id: UUID = Field(..., description="Workflow execution ID")
    step_name: str = Field(..., description="Name of completed step")
    step_number: int = Field(..., description="Step number (1-indexed)")
    total_steps: int = Field(..., description="Total number of steps")
    result: dict = Field(default_factory=dict, description="Step result data")


class WorkflowCompletedPayload(BaseModel):
    """Payload for WORKFLOW_COMPLETED events."""

    workflow_id: UUID = Field(..., description="Workflow execution ID")
    duration_seconds: float = Field(..., description="Total execution duration")
    result: dict = Field(default_factory=dict, description="Workflow result data")


class WorkflowFailedPayload(BaseModel):
    """Payload for WORKFLOW_FAILED events."""

    workflow_id: UUID = Field(..., description="Workflow execution ID")
    error_message: str = Field(..., description="Error message")
    failed_step: str | None = Field(default=None, description="Step that failed")
    recoverable: bool = Field(default=False, description="Whether error is recoverable")
