"""Load Value node for reading data from the project data store."""

from typing import Any, Literal

from flowire_sdk import BaseNode, BaseNodeOutput, NodeExecutionContext, NodeMetadata
from pydantic import BaseModel, Field


class LoadValueInput(BaseModel):
    """Input schema for Load Value node."""

    key: str = Field(..., description="Key to load from the data store")
    type: Literal["auto", "string", "number", "boolean"] = Field(
        "auto",
        description="Expected type to cast the value to",
    )
    default: str | None = Field(None, description="Default value if key is not found")


class LoadValueOutput(BaseNodeOutput):
    """Output schema for Load Value node."""

    key: str = Field(..., description="The key that was looked up")
    value: Any = Field(None, description="The loaded value (cast to the requested type)")
    found: bool = Field(..., description="Whether the key was found in the data store")

    def model_dump(self, **kwargs: Any) -> dict[str, Any]:
        """Always include 'value' even when None (executor uses exclude_none=True)."""
        result = super().model_dump(**kwargs)
        if "value" not in result:
            result["value"] = None
        return result


class LoadValueNode(BaseNode):
    """Load a value from the project data store."""

    input_schema = LoadValueInput
    output_schema = LoadValueOutput

    metadata = NodeMetadata(
        name="Load Value",
        description="Load a value from the project data store. Supports type casting.",
        category="data",
        icon="📂",
        color="#2196F3",
    )

    async def execute_logic(
        self,
        validated_inputs: dict[str, Any],
        context: NodeExecutionContext,
    ) -> LoadValueOutput:
        key = validated_inputs["key"]
        cast_type = validated_inputs["type"]
        default = validated_inputs.get("default")

        raw = await context.get_data_store_value(key)
        found = raw is not None

        if raw is None:
            raw = default

        if raw is None:
            return LoadValueOutput(key=key, value=None, found=found)

        value = self._cast(raw, cast_type)
        return LoadValueOutput(key=key, value=value, found=found)

    def _cast(self, raw: str, cast_type: str) -> Any:
        if cast_type == "string":
            return raw
        elif cast_type == "number":
            try:
                return int(raw) if "." not in raw else float(raw)
            except ValueError as e:
                raise ValueError(f"Cannot cast '{raw}' to number") from e
        elif cast_type == "boolean":
            if raw.lower() in ("true", "1", "yes"):
                return True
            elif raw.lower() in ("false", "0", "no"):
                return False
            raise ValueError(f"Cannot cast '{raw}' to boolean")
        else:  # auto
            # Try boolean
            if raw.lower() in ("true", "false"):
                return raw.lower() == "true"
            # Try number
            try:
                return int(raw) if "." not in raw else float(raw)
            except ValueError:
                pass
            return raw
