from __future__ import annotations

import sys
from typing import Any, Iterator, Literal, TypedDict

if sys.version_info <= (3, 11):
    from typing_extensions import Unpack
else:
    from typing import Unpack

HttpMethod = Literal["GET", "HEAD", "OPTIONS", "DELETE", "POST", "PUT", "PATCH"]
IMPERSONATE = Literal[
        "chrome_144", "chrome_145",
        "edge_144", "edge_145",
        "opera_126", "opera_127",
        "safari_18.5", "safari_26",
        "firefox_140", "firefox_146",
        "random",
    ]  # fmt: skip
IMPERSONATE_OS = Literal["android", "ios", "linux", "macos", "windows", "random"]

class RequestParams(TypedDict, total=False):
    auth: tuple[str, str | None] | None
    auth_bearer: str | None
    params: dict[str, str] | None
    headers: dict[str, str] | None
    cookies: dict[str, str] | None
    timeout: float | None
    content: bytes | None
    data: dict[str, Any] | None
    json: Any | None
    files: dict[str, str] | None

class ClientRequestParams(RequestParams):
    impersonate: IMPERSONATE | None
    impersonate_os: IMPERSONATE_OS | None
    verify: bool | None
    ca_cert_file: str | None

class Response:
    @property
    def content(self) -> bytes: ...
    @property
    def cookies(self) -> dict[str, str]: ...
    @property
    def headers(self) -> dict[str, str]: ...
    @property
    def status_code(self) -> int: ...
    @property
    def url(self) -> str: ...
    @property
    def encoding(self) -> str: ...
    @encoding.setter
    def encoding(self, encoding: str) -> None: ...
    @property
    def text(self) -> str: ...
    def json(self) -> Any: ...
    def read(self) -> bytes: ...
    def iter_content(self, chunk_size: int = 1) -> bytes | None: ...
    def iter_lines(self) -> list[str] | None: ...
    def raise_for_status(self) -> None: ...
    def stream(self) -> Iterator[bytes]: ...
    @property
    def text_markdown(self) -> str: ...
    @property
    def text_plain(self) -> str: ...
    @property
    def text_rich(self) -> str: ...

class AsyncResponse:
    """Async HTTP response object."""
    @property
    def content(self) -> bytes: ...
    @property
    def cookies(self) -> dict[str, str]: ...
    @property
    def headers(self) -> dict[str, str]: ...
    @property
    def status_code(self) -> int: ...
    @property
    def url(self) -> str: ...
    @property
    def encoding(self) -> str: ...
    @property
    def text(self) -> str: ...
    def json(self) -> Any: ...
    def read(self) -> bytes: ...
    def iter_content(self, chunk_size: int = 1) -> bytes | None: ...
    def iter_lines(self) -> list[str] | None: ...
    def raise_for_status(self) -> None: ...
    def __aiter__(self) -> AsyncResponseStream: ...
    def aread(self) -> bytes: ...
    def acontent(self) -> bytes: ...
    def atext(self) -> str: ...
    def ajson(self) -> Any: ...
    def aiter_content(self, chunk_size: int = 1) -> bytes | None: ...
    def aiter_lines(self) -> list[str] | None: ...

class AsyncResponseStream:
    """Async iterator for streaming response content."""
    def __anext__(self) -> bytes | None: ...

class RAsyncClient:
    """Async HTTP client that can impersonate web browsers."""
    def __init__(
        self,
        auth: tuple[str, str | None] | None = None,
        auth_bearer: str | None = None,
        params: dict[str, str] | None = None,
        headers: dict[str, str] | None = None,
        timeout: float | None = None,
        cookie_store: bool | None = True,
        referer: bool | None = True,
        proxy: str | None = None,
        impersonate: IMPERSONATE | None = None,
        impersonate_os: IMPERSONATE_OS | None = None,
        follow_redirects: bool | None = True,
        max_redirects: int | None = 20,
        verify: bool | None = True,
        ca_cert_file: str | None = None,
        https_only: bool | None = False,
        http2_only: bool | None = False,
    ): ...
    @property
    def headers(self) -> dict[str, str]: ...
    @headers.setter
    def headers(self, headers: dict[str, str]) -> None: ...
    def headers_update(self, headers: dict[str, str]) -> None: ...
    def get_cookies(self, url: str) -> dict[str, str]: ...
    def set_cookies(self, url: str, cookies: dict[str, str]) -> None: ...
    @property
    def proxy(self) -> str | None: ...
    @proxy.setter
    def proxy(self, proxy: str) -> None: ...
    @property
    def impersonate(self) -> str | None: ...
    @impersonate.setter
    def impersonate(self, impersonate: IMPERSONATE) -> None: ...
    @property
    def impersonate_os(self) -> str | None: ...
    @impersonate_os.setter
    def impersonate_os(self, impersonate: IMPERSONATE_OS) -> None: ...
    def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    def get(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    def head(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    def options(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    def post(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    def put(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...

class RClient:
    def __init__(
        self,
        auth: tuple[str, str | None] | None = None,
        auth_bearer: str | None = None,
        params: dict[str, str] | None = None,
        headers: dict[str, str] | None = None,
        timeout: float | None = None,
        cookie_store: bool | None = True,
        referer: bool | None = True,
        proxy: str | None = None,
        impersonate: IMPERSONATE | None = None,
        impersonate_os: IMPERSONATE_OS | None = None,
        follow_redirects: bool | None = True,
        max_redirects: int | None = 20,
        verify: bool | None = True,
        ca_cert_file: str | None = None,
        https_only: bool | None = False,
        http2_only: bool | None = False,
    ): ...
    @property
    def headers(self) -> dict[str, str]: ...
    @headers.setter
    def headers(self, headers: dict[str, str]) -> None: ...
    def headers_update(self, headers: dict[str, str]) -> None: ...
    def get_cookies(self, url: str) -> dict[str, str]: ...
    def set_cookies(self, url: str, cookies: dict[str, str]) -> None: ...
    @property
    def proxy(self) -> str | None: ...
    @proxy.setter
    def proxy(self, proxy: str) -> None: ...
    @property
    def impersonate(self) -> str | None: ...
    @impersonate.setter
    def impersonate(self, impersonate: IMPERSONATE) -> None: ...
    @property
    def impersonate_os(self) -> str | None: ...
    @impersonate_os.setter
    def impersonate_os(self, impersonate: IMPERSONATE_OS) -> None: ...
    def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
    def get(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
    def head(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
    def options(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
    def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
    def post(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
    def put(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
    def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...

class Client(RClient):
    """HTTP client that can impersonate web browsers."""
    def __enter__(self) -> Client: ...
    def __exit__(self, *args) -> None: ...

class AsyncClient(RAsyncClient):
    """Fully async HTTP client that can impersonate web browsers."""
    async def __aenter__(self) -> AsyncClient: ...
    async def __aexit__(self, *args) -> None: ...
    async def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    async def get(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    async def head(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    async def options(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    async def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    async def post(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    async def put(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...
    async def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> AsyncResponse: ...

def request(method: HttpMethod, url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
def get(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
def head(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
def options(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
def delete(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
def post(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
def put(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
def patch(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...

# Exception types

class RequestException(Exception):
    """Base exception for all primp errors."""
    url: str | None
    def __init__(self, message: str, url: str | None) -> None: ...

# PrimpError is an alias for RequestException
PrimpError = RequestException

class HTTPError(RequestException):
    """HTTP error for unsuccessful response status codes (4xx/5xx)."""
    status_code: int
    reason: str | None
    def __init__(self, message: str, status_code: int, url: str | None, reason: str | None) -> None: ...

class ConnectionError(RequestException):
    """Connection-related errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

class Timeout(RequestException):
    """Timeout errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

class TooManyRedirects(RequestException):
    """Too many redirects."""
    def __init__(self, message: str, url: str | None) -> None: ...

class RequestError(RequestException):
    """Request building or sending errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

# New parent exceptions for better hierarchy

class BodyError(RequestException):
    """Body-related errors (parent for body-specific errors)."""
    def __init__(self, message: str, url: str | None) -> None: ...

class DecodeError(RequestException):
    """Decoding errors (parent for decode-specific errors)."""
    def __init__(self, message: str, url: str | None) -> None: ...

class JSONError(RequestException):
    """JSON-related errors (parent for JSON-specific errors)."""
    def __init__(self, message: str, url: str | None) -> None: ...

# Body-related exceptions (under BodyError)

class StreamConsumedError(BodyError):
    """Stream consumed errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

class ChunkedEncodingError(BodyError):
    """Chunked encoding errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

# Decode-related exceptions (under DecodeError)

class ContentDecodingError(DecodeError):
    """Content decoding errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

# JSON-related exceptions (under JSONError)

class InvalidJSONError(JSONError):
    """JSON-related errors for invalid JSON in requests."""
    def __init__(self, message: str, url: str | None) -> None: ...

class JSONDecodeError(JSONError):
    """JSON decoding errors in response body."""
    def __init__(self, message: str, url: str | None) -> None: ...

# Connection-related exceptions (under ConnectionError)

class ProxyError(ConnectionError):
    """Proxy-related errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

class SSLError(ConnectionError):
    """SSL/TLS errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

class ConnectTimeout(ConnectionError):
    """Connection timeout errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

# Timeout-related exceptions (under Timeout)

class ReadTimeout(Timeout):
    """Read timeout errors."""
    def __init__(self, message: str, url: str | None) -> None: ...

# URL and Header exceptions (under RequestError)

class InvalidURL(RequestError):
    """Invalid URL (includes missing/invalid schema errors)."""
    def __init__(self, message: str, url: str | None) -> None: ...

class InvalidHeader(RequestError):
    """Invalid HTTP header."""
    def __init__(self, message: str, url: str | None) -> None: ...
