from enum import Enum
from typing import Any, Dict, List, Optional, Union

from pydantic import BaseModel

from crewai.utilities.events.base_events import BaseEvent

class LLMEventBase(BaseEvent):
    task_name: Optional[str] = None
    task_id: Optional[str] = None

    agent_id: Optional[str] = None
    agent_role: Optional[str] = None

    def __init__(self, **data):
        super().__init__(**data)
        self._set_agent_params(data)
        self._set_task_params(data)

    def _set_agent_params(self, data: Dict[str, Any]):
        task = data.get("from_task", None)
        agent = task.agent if task else data.get("from_agent", None)

        if not agent:
            return

        self.agent_id = agent.id
        self.agent_role = agent.role

    def _set_task_params(self, data: Dict[str, Any]):
        if "from_task" in data and (task := data["from_task"]):
            self.task_id = task.id
            self.task_name = task.name

class LLMCallType(Enum):
    """Type of LLM call being made"""

    TOOL_CALL = "tool_call"
    LLM_CALL = "llm_call"


class LLMCallStartedEvent(LLMEventBase):
    """Event emitted when a LLM call starts

    Attributes:
        messages: Content can be either a string or a list of dictionaries that support
            multimodal content (text, images, etc.)
    """

    type: str = "llm_call_started"
    messages: Optional[Union[str, List[Dict[str, Any]]]] = None
    tools: Optional[List[dict[str, Any]]] = None
    callbacks: Optional[List[Any]] = None
    available_functions: Optional[Dict[str, Any]] = None


class LLMCallCompletedEvent(LLMEventBase):
    """Event emitted when a LLM call completes"""

    type: str = "llm_call_completed"
    messages: str | list[dict[str, Any]] | None = None
    response: Any
    call_type: LLMCallType

class LLMCallFailedEvent(LLMEventBase):
    """Event emitted when a LLM call fails"""

    error: str
    type: str = "llm_call_failed"


class FunctionCall(BaseModel):
    arguments: str
    name: Optional[str] = None


class ToolCall(BaseModel):
    id: Optional[str] = None
    function: FunctionCall
    type: Optional[str] = None
    index: int


class LLMStreamChunkEvent(LLMEventBase):
    """Event emitted when a streaming chunk is received"""

    type: str = "llm_stream_chunk"
    chunk: str
    tool_call: Optional[ToolCall] = None
