import uuid
from collections.abc import Mapping
from enum import StrEnum
from typing import Any, Union

from pydantic import BaseModel, Field

from core.tools.entities.tool_entities import ToolInvokeMessage, ToolProviderType


class AgentToolEntity(BaseModel):
    """
    Agent Tool Entity.
    """

    provider_type: ToolProviderType
    provider_id: str
    tool_name: str
    tool_parameters: dict[str, Any] = Field(default_factory=dict)
    plugin_unique_identifier: str | None = None
    credential_id: str | None = None


class AgentPromptEntity(BaseModel):
    """
    Agent Prompt Entity.
    """

    first_prompt: str
    next_iteration: str


class AgentScratchpadUnit(BaseModel):
    """
    Agent First Prompt Entity.
    """

    class Action(BaseModel):
        """
        Action Entity.
        """

        action_name: str
        action_input: Union[dict, str]

        def to_dict(self):
            """
            Convert to dictionary.
            """
            return {
                "action": self.action_name,
                "action_input": self.action_input,
            }

    agent_response: str | None = None
    thought: str | None = None
    action_str: str | None = None
    observation: str | None = None
    action: Action | None = None

    def is_final(self) -> bool:
        """
        Check if the scratchpad unit is final.
        """
        return self.action is None or (
            "final" in self.action.action_name.lower() and "answer" in self.action.action_name.lower()
        )


class AgentEntity(BaseModel):
    """
    Agent Entity.
    """

    class Strategy(StrEnum):
        """
        Agent Strategy.
        """

        CHAIN_OF_THOUGHT = "chain-of-thought"
        FUNCTION_CALLING = "function-calling"

    provider: str
    model: str
    strategy: Strategy
    prompt: AgentPromptEntity | None = None
    tools: list[AgentToolEntity] | None = None
    max_iteration: int = 10


class AgentInvokeMessage(ToolInvokeMessage):
    """
    Agent Invoke Message.
    """

    pass


class ExecutionContext(BaseModel):
    """Execution context containing trace and audit information.

    This context carries all the IDs and metadata that are not part of
    the core business logic but needed for tracing, auditing, and
    correlation purposes.
    """

    user_id: str | None = None
    app_id: str | None = None
    conversation_id: str | None = None
    message_id: str | None = None
    tenant_id: str | None = None

    @classmethod
    def create_minimal(cls, user_id: str | None = None) -> "ExecutionContext":
        """Create a minimal context with only essential fields."""
        return cls(user_id=user_id)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for passing to legacy code."""
        return {
            "user_id": self.user_id,
            "app_id": self.app_id,
            "conversation_id": self.conversation_id,
            "message_id": self.message_id,
            "tenant_id": self.tenant_id,
        }

    def with_updates(self, **kwargs) -> "ExecutionContext":
        """Create a new context with updated fields."""
        data = self.to_dict()
        data.update(kwargs)

        return ExecutionContext(
            user_id=data.get("user_id"),
            app_id=data.get("app_id"),
            conversation_id=data.get("conversation_id"),
            message_id=data.get("message_id"),
            tenant_id=data.get("tenant_id"),
        )


class AgentLog(BaseModel):
    """
    Agent Log.
    """

    class LogType(StrEnum):
        """Type of agent log entry."""

        ROUND = "round"  # A complete iteration round
        THOUGHT = "thought"  # LLM thinking/reasoning
        TOOL_CALL = "tool_call"  # Tool invocation

    class LogMetadata(StrEnum):
        STARTED_AT = "started_at"
        FINISHED_AT = "finished_at"
        ELAPSED_TIME = "elapsed_time"
        TOTAL_PRICE = "total_price"
        TOTAL_TOKENS = "total_tokens"
        PROVIDER = "provider"
        CURRENCY = "currency"
        LLM_USAGE = "llm_usage"
        ICON = "icon"
        ICON_DARK = "icon_dark"

    class LogStatus(StrEnum):
        START = "start"
        ERROR = "error"
        SUCCESS = "success"

    id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="The id of the log")
    label: str = Field(..., description="The label of the log")
    log_type: LogType = Field(..., description="The type of the log")
    parent_id: str | None = Field(default=None, description="Leave empty for root log")
    error: str | None = Field(default=None, description="The error message")
    status: LogStatus = Field(..., description="The status of the log")
    data: Mapping[str, Any] = Field(..., description="Detailed log data")
    metadata: Mapping[LogMetadata, Any] = Field(default={}, description="The metadata of the log")


class AgentResult(BaseModel):
    """
    Agent execution result.
    """

    text: str = Field(default="", description="The generated text")
    files: list[Any] = Field(default_factory=list, description="Files produced during execution")
    usage: Any | None = Field(default=None, description="LLM usage statistics")
    finish_reason: str | None = Field(default=None, description="Reason for completion")
