from typing import Type, Optional

from core.typing import T


def get_value(
    source: dict,
    key: str,
    cast_type: Type[T],
    default: Optional[T] = None,
    list_delimiter: str = ",",
) -> Optional[T]:
    """
    Get value from source dictionary and cast it to a specified type.
    :param dict source: The source dictionary.
    :param str key: The name of the environment variable.
    :param Type[T] cast_type: A callable to cast the variable's value.
    :param Optional[T] default: The default value to return if the variable is not found. Defaults to None.
    :param str list_delimiter: The delimiter to split the value into a list. Defaults to ",".
    :return: The casted value, or None if the key is not found.
    :rtype: Optional[T]
    """

    if key not in source:
        return default

    value = source[key]
    if isinstance(
        value,
        cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__,
    ):
        # Handle list[int] case explicitly
        if hasattr(cast_type, "__origin__") and cast_type.__origin__ == list:
            subtype = cast_type.__args__[0] if hasattr(cast_type, "__args__") else None
            if subtype is not None:
                return [subtype(item) for item in value]
        return value

    try:
        if cast_type == bool:
            return value.lower() in ["true", "1"]

        if (
            cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__
        ) == list:
            if (
                not (value.startswith("[") and value.endswith("]"))
                and list_delimiter not in value
            ):
                raise ValueError(
                    "List values must be enclosed in square brackets or use a delimiter."
                )

            if value.startswith("[") and value.endswith("]"):
                value = value[1:-1]

            value = value.split(list_delimiter)
            subtype = cast_type.__args__[0] if hasattr(cast_type, "__args__") else None
            return [subtype(item) if subtype is not None else item for item in value]

        return cast_type(value)
    except (ValueError, TypeError):
        return default
