from typing import Any, TypedDict

from pydantic import BaseModel as PydanticBaseModel
from pydantic import ConfigDict, Field, create_model

TRUE_VALUES = ["true", "1", "t", "y", "yes"]


class SchemaField(TypedDict):
    name: str
    type: str
    description: str
    multiple: bool


class BaseModel(PydanticBaseModel):
    model_config = ConfigDict(populate_by_name=True)


def _get_type_annotation(type_str: str, *, multiple: bool) -> type:
    type_mapping = {
        "str": str,
        "int": int,
        "float": float,
        "bool": bool,
        "boolean": bool,
        "list": list[Any],
        "dict": dict[str, Any],
        "number": float,
        "text": str,
    }
    try:
        base_type = type_mapping[type_str]
    except KeyError as e:
        msg = f"Invalid type: {type_str}"
        raise ValueError(msg) from e
    if multiple:
        return list[base_type]  # type: ignore[valid-type]
    return base_type  # type: ignore[return-value]


def build_model_from_schema(schema: list[SchemaField]) -> type[PydanticBaseModel]:
    fields = {}
    for field in schema:
        field_name = field["name"]
        field_type_str = field["type"]
        description = field.get("description", "")
        multiple = field.get("multiple", False)
        multiple = coalesce_bool(multiple)
        field_type_annotation = _get_type_annotation(field_type_str, multiple=multiple)
        fields[field_name] = (field_type_annotation, Field(description=description))
    return create_model("OutputModel", **fields)


def coalesce_bool(value: Any) -> bool:
    """Coalesces the given value into a boolean.

    Args:
        value (Any): The value to be coalesced.

    Returns:
        bool: The coalesced boolean value.

    """
    if isinstance(value, bool):
        return value
    if isinstance(value, str):
        return value.lower() in TRUE_VALUES
    if isinstance(value, int):
        return bool(value)
    return False
