from collections.abc import Generator, Iterable
from contextlib import contextmanager
from io import BytesIO
from typing import NamedTuple
import datetime
import logging
import re

from django.contrib.auth.models import AbstractBaseUser
from django.core.cache.backends.base import BaseCache
from django.core.files import File
from django.db import models
from django.utils.functional import cached_property as cached_property
from django.utils.functional import classproperty
from taggit.managers import TaggableManager
from wagtail import hooks as hooks
from wagtail.coreutils import string_to_ascii as string_to_ascii
from wagtail.images.exceptions import (
    InvalidFilterSpecError as InvalidFilterSpecError,
)
from wagtail.images.exceptions import (
    UnknownOutputImageFormatError as UnknownOutputImageFormatError,
)
from wagtail.images.image_operations import (
    FilterOperation as FilterOperation,
)
from wagtail.images.image_operations import (
    FormatOperation as FormatOperation,
)
from wagtail.images.image_operations import (
    ImageTransform as ImageTransform,
)
from wagtail.images.image_operations import (
    TransformOperation as TransformOperation,
)
from wagtail.images.rect import Rect as Rect
from wagtail.images.utils import to_svg_safe_spec as to_svg_safe_spec
from wagtail.models import CollectionMember as CollectionMember
from wagtail.models import ReferenceIndex as ReferenceIndex
from wagtail.search import index as index
from wagtail.search.queryset import SearchableQuerySetMixin as SearchableQuerySetMixin
from wagtail.utils.file import hash_filelike as hash_filelike
from willow.image import Image as WillowImage

logger: logging.Logger
IMAGE_FORMAT_EXTENSIONS: dict[str, str]

class SourceImageIOError(IOError): ...

class ImageQuerySet(SearchableQuerySetMixin, models.QuerySet):
    def prefetch_renditions(self, *filters): ...

def get_upload_to(instance, filename): ...
def get_rendition_upload_to(instance, filename): ...
def get_rendition_storage(): ...

class ImageFileMixin:
    def is_stored_locally(self): ...
    file_size: int | None
    def get_file_size(self) -> int | None: ...
    @contextmanager
    def open_file(self) -> Generator[File]: ...
    @contextmanager
    def get_willow_image(self) -> Generator[WillowImage]: ...

class WagtailImageFieldFile(models.fields.files.ImageFieldFile):
    def get_image_dimensions(self): ...

class WagtailImageField(models.ImageField):
    attr_class = WagtailImageFieldFile

class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Model):
    title: models.CharField[str, str]
    file: WagtailImageField
    description: models.CharField[str, str]
    width: models.IntegerField[int, int]
    height: models.IntegerField[int, int]
    created_at: models.DateTimeField[datetime.datetime, datetime.datetime]
    uploaded_by_user: models.ForeignKey[AbstractBaseUser | None, AbstractBaseUser | None]
    tags: TaggableManager
    focal_point_x: models.PositiveIntegerField[int | None, int | None]
    focal_point_y: models.PositiveIntegerField[int | None, int | None]
    focal_point_width: models.PositiveIntegerField[int | None, int | None]
    focal_point_height: models.PositiveIntegerField[int | None, int | None]
    file_size: models.PositiveIntegerField[int | None, int | None]
    file_hash: models.CharField[str, str]
    objects: ImageQuerySet
    decorative: bool
    contextual_alt_text: str | None
    def __init__(self, *args, **kwargs) -> None: ...
    def get_file_hash(self): ...
    def get_upload_to(self, filename): ...
    def get_usage(self): ...
    @property
    def usage_url(self): ...
    search_fields: list[index.BaseField | index.RelatedFields]
    def __eq__(self, other): ...
    def __hash__(self): ...
    def get_rect(self): ...
    def get_focal_point(self): ...
    def has_focal_point(self): ...
    def set_focal_point(self, rect) -> None: ...
    def get_suggested_focal_point(self): ...
    @classmethod
    def get_rendition_model(cls): ...
    def clean_filter_for_svg(self, filter: Filter) -> Filter: ...
    def get_rendition(self, filter: Filter | str) -> AbstractRendition: ...
    def find_existing_rendition(self, filter: Filter) -> AbstractRendition: ...
    def create_rendition(self, filter: Filter) -> AbstractRendition: ...
    def get_renditions(self, *filters: Filter | str) -> dict[str, AbstractRendition]: ...
    def find_existing_renditions(self, *filters: Filter) -> dict[Filter, AbstractRendition]: ...
    def create_renditions(self, *filters: Filter) -> dict[Filter, AbstractRendition]: ...
    def generate_rendition_instance(self, filter: Filter, source: BytesIO) -> AbstractRendition: ...
    def generate_rendition_file(self, filter: Filter, *, source: File = None) -> File: ...
    def is_portrait(self): ...
    def is_landscape(self): ...
    def is_svg(self): ...
    @property
    def filename(self): ...
    @property
    def default_alt_text(self): ...
    def is_editable_by_user(self, user): ...
    class Meta:
        abstract: bool

class Image(AbstractImage):
    admin_form_fields: tuple[str, ...]
    class Meta(AbstractImage.Meta):
        verbose_name: str
        verbose_name_plural: str
        permissions: list[tuple[str, str]]

class Filter:
    spec_pattern: re.Pattern[str]
    pipe_spec_pattern: re.Pattern[str]
    expanding_spec_pattern: re.Pattern[str]
    pipe_expanding_spec_pattern: re.Pattern[str]
    spec: str
    def __init__(self, spec=None) -> None: ...
    @classmethod
    def expand_spec(self, spec: str | Iterable[str]) -> list[str]: ...
    @cached_property
    def operations(self): ...
    @property
    def transform_operations(self): ...
    @property
    def filter_operations(self): ...
    def get_transform(self, image, size=None): ...
    @contextmanager
    def get_willow_image(self, image: AbstractImage, source: File = None): ...
    def run(self, image: AbstractImage, output: BytesIO, source: File = None): ...
    def get_cache_key(self, image): ...
    def __eq__(self, value): ...
    def __hash__(self): ...

class ResponsiveImage:
    renditions: list[AbstractRendition]
    attrs: dict[str, str | int | bool] | None
    def __init__(
        self, renditions: dict[str, AbstractRendition], attrs: dict[str, str | int | bool] | None = None
    ) -> None: ...
    @classmethod
    def get_width_srcset(cls, renditions_list: list[AbstractRendition]): ...
    def __html__(self): ...
    def __bool__(self) -> bool: ...
    def __eq__(self, other: ResponsiveImage): ...

class FileFormat(NamedTuple):
    name: str
    mime_type: str

class Picture(ResponsiveImage):
    source_format_order: list[FileFormat]
    formats: dict[str, list[AbstractRendition]]
    def __init__(
        self, renditions: dict[str, AbstractRendition], attrs: dict[str, str | int | bool] | None = None
    ) -> None: ...
    def get_formats(self, renditions: dict[str, AbstractRendition]) -> dict[str, list[AbstractRendition]]: ...
    def get_fallback_format(self): ...
    def __html__(self): ...

class AbstractRendition(ImageFileMixin, models.Model):
    filter_spec: models.CharField[str, str]
    file: WagtailImageField
    width: models.IntegerField[int, int]
    height: models.IntegerField[int, int]
    focal_point_key: models.CharField[str, str]
    wagtail_reference_index_ignore: bool
    @property
    def url(self): ...
    @property
    def alt(self): ...
    @property
    def attrs(self): ...
    @property
    def attrs_dict(self): ...
    @property
    def full_url(self): ...
    @property
    def filter(self): ...
    @cached_property
    def focal_point(self): ...
    @property
    def background_position_style(self): ...
    @property
    def background_position_x(self): ...
    @property
    def background_position_y(self): ...
    def img_tag(self, extra_attributes=None): ...
    def __html__(self): ...
    def get_upload_to(self, filename): ...
    @classmethod
    def check(cls, **kwargs): ...
    @staticmethod
    def construct_cache_key(image, filter_cache_key, filter_spec): ...
    @classproperty
    def cache_backend(cls) -> BaseCache: ...
    def get_cache_key(self): ...
    def purge_from_cache(self) -> None: ...
    class Meta:
        abstract: bool

class Rendition(AbstractRendition):
    image: models.ForeignKey[Image, Image]
    class Meta:
        unique_together: tuple[tuple[str, ...], ...]
