from enum import Enum
from typing import Any, Literal, Optional

from pydantic import BaseModel, Field

from ..typings import PydanticModelConfig


class AgenticActionWarningLevel(str, Enum):
    SAFE = "SAFE"
    MUTATION = "MUTATION"
    DESTRUCTION = "DESTRUCTION"


class AgenticActionPayload(BaseModel):
    model_config = PydanticModelConfig.default()
    message: str = Field(
        description="A simple, human-friendly message explaining why you want to do this action (used for confirmation). Don’t include IDs, keys, or technical terms due to most user may be complete out-of-context and non-developer."
    )
    warning_level: AgenticActionWarningLevel = Field(
        description="The level of warning associated with the action; When no data mutation/or descruction is involved, use SAFE; otherwise, use MUTATION or DESTRUCTION accordingly"
    )
    object: Literal["agentic_action_payload"] = Field(
        default="agentic_action_payload",
        description="A string representing the object type.",
    )

    @classmethod
    def extract_agentic_action_payload(
        cls, args: Any
    ) -> Optional["AgenticActionPayload"]:
        def _normalize_candidate(candidate: Any) -> dict[str, Any] | None:
            if not isinstance(candidate, dict):
                return None
            if (
                "object" not in candidate
                or candidate["object"] != "agentic_action_payload"
            ):  # a literal string name in the dict, to identify the object type
                return None
            if "message" not in candidate:
                raise ValueError("agentic_action_payload missing 'message' field")
            if "warning_level" in candidate:
                warning_level = candidate["warning_level"]
            elif "warningLevel" in candidate:
                warning_level = candidate["warningLevel"]
            else:
                raise ValueError(
                    "agentic_action_payload missing 'warning_level'/'warningLevel' field"
                )
            return {"message": candidate["message"], "warning_level": warning_level}

        def _find_candidate(value: Any) -> dict[str, Any] | None:
            normalized = _normalize_candidate(value)
            if normalized is not None:
                return normalized
            if isinstance(value, dict):
                for v in value.values():
                    found = _find_candidate(v)
                    if found is not None:
                        return found
            elif isinstance(value, list):
                for v in value:
                    found = _find_candidate(v)
                    if found is not None:
                        return found
            return None

        candidate = _find_candidate(args)
        if candidate is None:
            return None
        return AgenticActionPayload(**candidate)


class AgentDeferredReason(BaseModel):
    model_config = PydanticModelConfig.default()
    reasons: list[str] = Field(
        default_factory=list, description="The reasons of the deferred tool request."
    )


class AgentDeferredToolFormDescription(BaseModel):
    model_config = PydanticModelConfig.default()
    markdown: Optional[str] = Field(default=None)


class AgentDeferredToolFormLabel(BaseModel):
    model_config = PydanticModelConfig.default()
    label: Optional[str] = Field(default=None)
    description: Optional[AgentDeferredToolFormDescription] = Field(default=None)


class AgentDeferredToolFormItemTextInput(BaseModel):
    model_config = PydanticModelConfig.default()
    placeholder: Optional[str] = Field(default=None)
    value: Optional[str] = Field(default=None)
    max_len: Optional[int] = Field(default=None)
    min_len: Optional[int] = Field(default=None)


class AgentDeferredToolFormItemTextListInput(BaseModel):
    model_config = PydanticModelConfig.default()
    placeholder: Optional[str] = Field(default=None)
    value: Optional[list[str]] = Field(default=None)
    str_max_len: Optional[int] = Field(default=None)
    str_min_len: Optional[int] = Field(default=None)
    list_max_len: Optional[int] = Field(default=None)
    list_min_len: Optional[int] = Field(default=None)


class AgentDeferredToolFormItemNumberInput(BaseModel):
    model_config = PydanticModelConfig.default()
    placeholder: Optional[str] = Field(default=None)
    value: Optional[float] = Field(default=None)
    gt: Optional[float] = Field(default=None)
    lt: Optional[float] = Field(default=None)
    gte: Optional[float] = Field(default=None)
    lte: Optional[float] = Field(default=None)


class AgentDeferredToolFormItemSelectOption(BaseModel):
    model_config = PydanticModelConfig.default()
    label_markdown: Optional[str] = Field(default=None)
    value: str | float


class AgentDeferredToolFormItemMultiSelect(BaseModel):
    model_config = PydanticModelConfig.default()
    options: list[AgentDeferredToolFormItemSelectOption] = Field(default_factory=list)
    values: Optional[list[str | float]] = Field(
        default=None, description="filled by user, used for results form"
    )


class AgentDeferredToolFormItemSingleSelect(BaseModel):
    model_config = PydanticModelConfig.default()
    options: list[AgentDeferredToolFormItemSelectOption] = Field(default_factory=list)
    value: Optional[str | float] = Field(
        default=None, description="filled by user, used for results form"
    )


class AgentDeferredToolFormItemCheckbox(BaseModel):
    model_config = PydanticModelConfig.default()
    checked_markdown: Optional[str] = Field(default=None)
    unchecked_markdown: Optional[str] = Field(default=None)
    value: Optional[bool] = Field(default=None)


class AgentDeferredToolFormItemType(str, Enum):
    TEXTAREA = "TEXTAREA"
    TEXT_INPUT = "TEXT_INPUT"
    TEXT_LIST_INPUT = "TEXT_LIST_INPUT"
    INT_INPUT = "INT_INPUT"
    FLOAT_INPUT = "FLOAT_INPUT"
    SINGLE_SELECT = "SINGLE_SELECT"
    MULTI_SELECT = "MULTI_SELECT"
    CHECKBOX = "CHECKBOX"


class AgentDeferredToolFormItem(AgentDeferredReason):
    model_config = PydanticModelConfig.default()
    item_type: AgentDeferredToolFormItemType
    label: AgentDeferredToolFormLabel
    text_input: Optional[AgentDeferredToolFormItemTextInput] = Field(
        default=None, description="For item of type TEXTAREA or TEXT_INPUT"
    )
    text_list_input: Optional[AgentDeferredToolFormItemTextListInput] = Field(
        default=None, description="For item of type TEXT_LIST_INPUT"
    )
    number_input: Optional[AgentDeferredToolFormItemNumberInput] = Field(
        default=None, description="For item of type INT_INPUT or FLOAT_INPUT"
    )
    single_select: Optional[AgentDeferredToolFormItemSingleSelect] = Field(
        default=None, description="For item of type SINGLE_SELECT"
    )
    multi_select: Optional[AgentDeferredToolFormItemMultiSelect] = Field(
        default=None, description="For item of type MULTI_SELECT"
    )
    checkbox: Optional[AgentDeferredToolFormItemCheckbox] = Field(
        default=None, description="For item of type CHECKBOX"
    )

    @property
    def value(self) -> Any:
        if self.item_type in {
            AgentDeferredToolFormItemType.TEXTAREA,
            AgentDeferredToolFormItemType.TEXT_INPUT,
        }:
            return self.text_input.value if self.text_input else None
        elif self.item_type == AgentDeferredToolFormItemType.TEXT_LIST_INPUT:
            return self.text_list_input.value if self.text_list_input else None
        elif self.item_type in {
            AgentDeferredToolFormItemType.INT_INPUT,
            AgentDeferredToolFormItemType.FLOAT_INPUT,
        }:
            return self.number_input.value if self.number_input else None
        elif self.item_type == AgentDeferredToolFormItemType.SINGLE_SELECT:
            return self.single_select.value if self.single_select else None
        elif self.item_type == AgentDeferredToolFormItemType.MULTI_SELECT:
            return self.multi_select.values if self.multi_select else None
        elif self.item_type == AgentDeferredToolFormItemType.CHECKBOX:
            return self.checkbox.value if self.checkbox else None
        return None


class AgentDeferredToolRequestsForm(AgentDeferredReason):
    model_config = PydanticModelConfig.default()
    title: Optional[str] = Field(default=None)
    description: Optional[AgentDeferredToolFormDescription] = Field(default=None)
    inputs: list[AgentDeferredToolFormItem]


class AgentDeferredToolResultsForm(AgentDeferredReason):
    model_config = PydanticModelConfig.default()
    inputs: list[AgentDeferredToolFormItem] = Field(default_factory=list)


class AgentDeferredToolPayloadBase(AgentDeferredReason):
    model_config = PydanticModelConfig.default()

    # metadata within metadata
    metadata: Optional[dict[str, Any]] = Field(
        default=None,
        description="Optional metadata associated with the deferred tool request.",
    )
    form: Optional[AgentDeferredToolRequestsForm | AgentDeferredToolResultsForm]

    def get_form_value_by_deferred_reason(self, reason: str) -> Any:
        if self.form is None:
            return None
        for item in self.form.inputs:
            if reason in item.reasons:
                return item.value
        return None


class AgentDeferredToolRequestsPayload(AgentDeferredToolPayloadBase):
    model_config = PydanticModelConfig.default()

    question: Optional[str] = Field(
        default=None,
        description="The question that require user to answer before the agent can continue the action. This is used for deferred tool request.",
    )
    default_answer: Optional[str] = Field(
        default=None,
        description="An optional default answer to suggest to the user.",
    )
    form: Optional[AgentDeferredToolRequestsForm] = Field(
        default=None,
        description="The form that the user need to fill out to answer the question.",
    )


class AgentDeferredToolResultsPayload(AgentDeferredToolPayloadBase):
    model_config = PydanticModelConfig.default()
    answer: Optional[str] = Field(
        default=None,
        description="The answer provided by the user to response the  deferred tool request's question.",
    )
    form: Optional[AgentDeferredToolResultsForm] = Field(
        default=None,
        description="The form filled out by the user to response the deferred tool request's form.",
    )
