from __future__ import annotations

from os import PathLike
from typing import (
    IO,
    Any,
    Dict,
    List,
    Mapping,
    Optional,
    Sequence,
    Tuple,
    Type,
    TypeVar,
    Union,
)

from typing_extensions import Literal, TypeAlias, TypedDict, override

import httpx
import pydantic

Query: TypeAlias = Mapping[str, object]
Body: TypeAlias = object
Headers: TypeAlias = Mapping[str, str]
ModelT = TypeVar("ModelT", bound=pydantic.BaseModel)
_T = TypeVar("_T")

FileContent: TypeAlias = Union[IO[bytes], bytes, "PathLike[str]"]
FileTypes: TypeAlias = Union[
    FileContent,
    Tuple[Optional[str], FileContent],
    Tuple[Optional[str], FileContent, Optional[str]],
    Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]],
]
RequestFiles: TypeAlias = Union[
    Mapping[str, FileTypes],
    Sequence[Tuple[str, FileTypes]],
]

HttpxFileContent: TypeAlias = Union[IO[bytes], bytes]
HttpxFileTypes: TypeAlias = Union[
    HttpxFileContent,
    Tuple[Optional[str], HttpxFileContent],
    Tuple[Optional[str], HttpxFileContent, Optional[str]],
    Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]],
]
HttpxRequestFiles: TypeAlias = Union[
    Mapping[str, HttpxFileTypes],
    Sequence[Tuple[str, HttpxFileTypes]],
]

FileInput: TypeAlias = Union[
    str,
    "PathLike[str]",
    IO[bytes],
    bytes,
    Tuple[str, bytes],
]
"""User-facing file input type.

- ``str`` starting with ``https://`` → public URL, passed directly to backend.
- ``str`` starting with ``file_`` → existing file_id, passed directly.
- ``Path`` object → local file, auto-uploaded via presigned URL.
- ``IO[bytes]`` (e.g. ``open('f.jpg', 'rb')``) → auto-uploaded.
- ``bytes`` → auto-uploaded.
- ``(filename, bytes)`` tuple → auto-uploaded with explicit filename.

For local files, always use ``open('file.jpg', 'rb')`` or ``Path('file.jpg')``.
Plain string paths like ``'./file.jpg'`` are NOT supported — use ``Path()`` instead.
"""


class RequestOptions(TypedDict, total=False):
    headers: Headers
    max_retries: int
    timeout: float | httpx.Timeout | None
    params: Query
    extra_json: Mapping[str, object]


class NotGiven:
    """Sentinel to distinguish 'not provided' from ``None``."""

    def __bool__(self) -> Literal[False]:
        return False

    @override
    def __repr__(self) -> str:
        return "NOT_GIVEN"


NOT_GIVEN = NotGiven()


class Omit:
    """Sentinel to explicitly remove a default header/field."""

    def __bool__(self) -> Literal[False]:
        return False


OMIT = Omit()


def is_given(v: object) -> bool:
    return not isinstance(v, (NotGiven, Omit))
