from collections import OrderedDict
from collections.abc import Callable, Iterable, Iterator, Mapping, MutableSequence
from typing import Any, overload
import itertools

from django import forms
from django.core import checks
from django.core.exceptions import ValidationError
from django.forms.utils import ErrorList, _DataT, _FilesT
from django.utils.functional import cached_property
from telepath import Adapter

from .base import Block, BoundBlock, DeclarativeSubBlocksMetaclass
from .definition_lookup import BlockDefinitionLookup, BlockDefinitionLookupBuilder

__all__ = [
    "BaseStreamBlock",
    "StreamBlock",
    "StreamValue",
    "StreamBlockValidationError",
]

class StreamBlockValidationError(ValidationError):
    non_block_errors: ErrorList
    block_errors: dict[int, ValidationError]
    def __init__(
        self,
        block_errors: dict[int, ValidationError | ErrorList | list[ValidationError]] | None = None,
        non_block_errors: ErrorList | list[str | ValidationError] | None = None,
    ) -> None: ...
    def as_json_data(self) -> dict[str, Any]: ...

class BaseStreamBlock(Block):
    search_index: bool
    child_blocks: OrderedDict[str, Block]
    _constructor_kwargs: dict[str, Any]
    MUTABLE_META_ATTRIBUTES: list[str]

    class Meta:
        icon: str
        default: list[Any]
        required: bool
        form_classname: str | None
        min_num: int | None
        max_num: int | None
        block_counts: dict[str, dict[str, int]]
        collapsed: bool

    def __init__(
        self,
        local_blocks: Iterable[tuple[str, Block]] | None = None,
        search_index: bool = True,
        **kwargs: Any,
    ) -> None: ...
    @classmethod
    def construct_from_lookup(
        cls,
        lookup: BlockDefinitionLookup,
        child_blocks: list[tuple[str, int]] | None = None,
        **kwargs: Any,
    ) -> BaseStreamBlock: ...
    def empty_value(self, raw_text: str | None = None) -> StreamValue: ...
    def sorted_child_blocks(self) -> list[Block]: ...
    def grouped_child_blocks(self) -> itertools.groupby[str, Block]: ...
    def value_from_datadict(self, data: _DataT, files: _FilesT, prefix: str) -> StreamValue: ...
    def value_omitted_from_data(self, data: _DataT, files: _FilesT, prefix: str) -> bool: ...
    @property
    def required(self) -> bool: ...  # type: ignore[override]
    def clean(self, value: StreamValue, ignore_required_constraints: bool = False) -> StreamValue: ...
    def to_python(self, value: str | list[dict[str, Any]] | list[tuple[str, Any]] | StreamValue) -> StreamValue: ...
    def bulk_to_python(self, values: list[list[dict[str, Any]]]) -> list[StreamValue]: ...
    def get_prep_value(self, value: StreamValue | None) -> list[dict[str, Any]]: ...
    def normalize(self, value: str | list[dict[str, Any]] | list[tuple[str, Any]] | StreamValue) -> StreamValue: ...
    def get_form_state(self, value: StreamValue | None) -> list[dict[str, Any]]: ...
    def get_api_representation(
        self, value: StreamValue | None, context: dict[str, Any] | None = None
    ) -> list[dict[str, Any]]: ...
    def render_basic(self, value: StreamValue, context: dict[str, Any] | None = None) -> str: ...
    def get_searchable_content(self, value: StreamValue) -> list[str]: ...
    def extract_references(self, value: StreamValue) -> Iterable[tuple[type, str, str, str]]: ...
    def get_block_by_content_path(self, value: StreamValue, path_elements: list[str]) -> BoundBlock | None: ...
    def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]: ...
    def deconstruct_with_lookup(
        self, lookup: BlockDefinitionLookupBuilder
    ) -> tuple[str, list[Any], dict[str, Any]]: ...
    def check(self, **kwargs: Any) -> list[checks.Error]: ...
    @cached_property
    def _has_default(self) -> bool: ...

class StreamBlock(BaseStreamBlock, metaclass=DeclarativeSubBlocksMetaclass): ...

class StreamValue(MutableSequence[StreamValue.StreamChild]):
    stream_block: BaseStreamBlock
    is_lazy: bool
    raw_text: str | None
    _raw_data: list[dict[str, Any] | None]
    _bound_blocks: list[StreamValue.StreamChild | None]

    class StreamChild(BoundBlock):
        id: str | None
        @property
        def block_type(self) -> str: ...
        def __init__(self, *args: Any, id: str | None = None, **kwargs: Any) -> None: ...
        def get_prep_value(self) -> dict[str, Any]: ...
        def _as_tuple(self) -> tuple[str, Any] | tuple[str, Any, str]: ...

    class RawDataView(MutableSequence[dict[str, Any]]):
        stream_value: StreamValue
        def __init__(self, stream_value: StreamValue) -> None: ...
        @overload
        def __getitem__(self, i: int) -> dict[str, Any]: ...
        @overload
        def __getitem__(self, i: slice) -> list[dict[str, Any]]: ...
        def __getitem__(self, i: int | slice) -> dict[str, Any] | list[dict[str, Any]]: ...
        def __len__(self) -> int: ...
        def __setitem__(self, i: int, item: dict[str, Any]) -> None: ...  # type: ignore[override]
        def __delitem__(self, i: int) -> None: ...  # type: ignore[override]
        def insert(self, i: int, item: dict[str, Any]) -> None: ...

    class BlockNameLookup(Mapping[str, list[StreamValue.StreamChild] | StreamValue.StreamChild | None]):
        stream_value: StreamValue
        block_names: Iterable[str]
        find_all: bool
        def __init__(self, stream_value: StreamValue, find_all: bool = True) -> None: ...
        def __getitem__(self, block_name: str) -> list[StreamValue.StreamChild] | StreamValue.StreamChild | None: ...
        def __iter__(self) -> Iterator[str]: ...
        def __len__(self) -> int: ...

    def __init__(
        self,
        stream_block: BaseStreamBlock,
        stream_data: list[dict[str, Any]] | list[tuple[str, Any] | tuple[str, Any, str] | StreamChild],
        is_lazy: bool = False,
        raw_text: str | None = None,
    ) -> None: ...
    def _construct_stream_child(
        self,
        item: StreamValue.StreamChild | tuple[str, Any, str] | tuple[str, Any],
    ) -> StreamValue.StreamChild: ...
    @overload
    def __getitem__(self, i: int) -> StreamValue.StreamChild: ...
    @overload
    def __getitem__(self, i: slice) -> list[StreamValue.StreamChild]: ...
    def __getitem__(self, i: int | slice) -> StreamValue.StreamChild | list[StreamValue.StreamChild]: ...  # type: ignore[override]
    def __setitem__(self, i: int, item: StreamValue.StreamChild | tuple[str, Any] | tuple[str, Any, str]) -> None: ...  # type: ignore[override]
    def __delitem__(self, i: int) -> None: ...  # type: ignore[override]
    def __len__(self) -> int: ...
    def insert(self, i: int, item: StreamValue.StreamChild | tuple[str, Any] | tuple[str, Any, str]) -> None: ...
    @cached_property
    def raw_data(self) -> StreamValue.RawDataView: ...
    def _prefetch_blocks(self, type_name: str) -> None: ...
    def get_prep_value(self) -> list[dict[str, Any]]: ...
    def blocks_by_name(
        self, block_name: str | None = None
    ) -> list[StreamValue.StreamChild] | StreamValue.BlockNameLookup: ...
    def first_block_by_name(
        self, block_name: str | None = None
    ) -> StreamValue.StreamChild | None | StreamValue.BlockNameLookup: ...
    def __eq__(self, other: object) -> bool: ...
    def render_as_block(self, context: dict[str, Any] | None = None) -> str: ...
    def __html__(self) -> str: ...
    @staticmethod
    def _deserialize_pickle_value(
        app_label: str, model_name: str, field_name: str, field_value: list[dict[str, Any]]
    ) -> StreamValue: ...
    def __reduce__(self) -> tuple[Callable[..., StreamValue], tuple[str, str, str, list[dict[str, Any]]]]: ...

class StreamBlockAdapter(Adapter):
    js_constructor: str
    def js_args(self, block: StreamBlock) -> list[Any]: ...
    @cached_property
    def media(self) -> forms.Media: ...
