from typing import Any, Dict, List, Optional, Union

from pynamodb.attributes import Attribute
from pynamodb.expressions.condition import (
    BeginsWith, Between, Comparison, Contains, Exists, In, IsType, NotExists
)
from pynamodb.expressions.update import (
    AddAction, DeleteAction, RemoveAction, SetAction, ListRemoveAction
)


class _Operand(object):
    format_string: str
    short_attr_type: Optional[str]

    values: List[Path]

    def __init__(self, *values: Path) -> None: ...
    def serialize(self, placeholder_names: Dict[str, str], expression_attribute_values: Dict[str, str]) -> str: ...


class _NumericOperand(_Operand): ...


class _ListAppend(_Operand):
    def __init__(self, list1: Any, list2: Any) -> None: ...


class _ListAppendOperand(_Operand):
    def append(self, other: Any) -> _ListAppend: ...
    def prepend(self, other: Any) -> _ListAppend: ...


class _ConditionOperand(_Operand):
    def __eq__(self, other: Any) -> Comparison: ...  # type: ignore
    def __ne__(self, other: Any) -> Comparison: ...  # type: ignore
    def __lt__(self, other: Any) -> Comparison: ...
    def __le__(self, other: Any) -> Comparison: ...
    def __gt__(self, other: Any) -> Comparison: ...
    def __ge__(self, other: Any) -> Comparison: ...
    def between(self, lower: Any, upper: Any) -> Between: ...
    def is_in(self, *values: Any) -> In: ...


class _Size(_ConditionOperand):
    def __init__(self, path: Union[Path, Attribute]) -> None: ...


class _Increment(_Operand):
    def __init__(self, lhs: Union[Path, Attribute]) -> None: ...


class _Decrement(_Operand):
    def __init__(self, lhs: Union[Path, Attribute]) -> None: ...


class _IfNotExists(_Operand):
    def __init__(self, lhs: Union[Path, Attribute]) -> None: ...


class Value(_NumericOperand, _ListAppendOperand, _ConditionOperand):
    def __init__(self, value: Optional[Union[Path, Dict]], attribute=Optional[Attribute]) -> None: ...


class Path(_NumericOperand, _ListAppendOperand, _ConditionOperand):
    attribute: Union[Attribute, Path]
    short_attr_type: Optional[str]

    def __init__(self, attribute_or_path: Union[Attribute, Path, str]) -> None: ...
    @property
    def path(self) -> Path: ...
    def add(self, *values: Any) -> AddAction: ...
    def delete(self, *values: Any) -> DeleteAction: ...
    def exists(self) -> Exists: ...
    def does_not_exist(self) -> NotExists: ...
    def is_type(self, attr_type: str) -> IsType: ...
    def startswith(self, prefix: str) -> BeginsWith: ...
    def contains(self, item: Any) -> Contains: ...
    def set(self, value: Any) -> SetAction: ...
    def remove(self) -> RemoveAction: ...
    def remove_list_elements(self, *indexes: int) -> ListRemoveAction: ...
    def __getitem__(self, idx: Union[int, str]) -> Path: ...
