# AUTO-GENERATED FILE - DO NOT EDIT
# This file was automatically generated by the XDK build tool.
# Any manual changes will be overwritten on the next generation.

"""
Auto-generated stream client for the X API.

This module provides a client for interacting with the stream endpoints of the X API.
Real-time streaming operations return generators that yield data as it arrives.
Streaming connections are automatically managed with exponential backoff retry logic for robust handling.
All methods, parameters, and response models are generated from the OpenAPI specification.

Generated automatically - do not edit manually.
"""

from __future__ import annotations
from typing import (
    Dict,
    List,
    Optional,
    Any,
    Union,
    cast,
    TYPE_CHECKING,
    Iterator,
    Generator,
)
import requests
import time
import urllib.parse

from ..streaming import StreamConfig, StreamError, stream_with_retry


if TYPE_CHECKING:
    from ..client import Client
from .models import (
    PostsFirehoseJaResponse,
    PostsFirehoseResponse,
    PostsSample10Response,
    LikesComplianceResponse,
    PostsResponse,
    PostsFirehoseEnResponse,
    LabelsComplianceResponse,
    UsersComplianceResponse,
    PostsFirehoseKoResponse,
    PostsSampleResponse,
    PostsComplianceResponse,
    GetRuleCountsResponse,
    LikesSample10Response,
    GetRulesResponse,
    UpdateRulesRequest,
    UpdateRulesResponse,
    LikesFirehoseResponse,
    PostsFirehosePtResponse,
)


class StreamClient:
    """Streaming Client for stream operations"""


    def __init__(self, client: Client):
        self.client = client


    def posts_firehose_ja(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsFirehoseJaResponse, None, None]:
        """
        Stream Japanese Posts (Streaming)
        Streams all public Japanese-language Posts in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsFirehoseJaResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/firehose/stream/lang/ja"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsFirehoseJaResponse,
        )


    def posts_firehose(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsFirehoseResponse, None, None]:
        """
        Stream all Posts (Streaming)
        Streams all public Posts in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsFirehoseResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/firehose/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsFirehoseResponse,
        )


    def posts_sample10(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsSample10Response, None, None]:
        """
        Stream 10% sampled Posts (Streaming)
        Streams a 10% sample of public Posts in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsSample10Response: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/sample10/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsSample10Response,
        )


    def likes_compliance(
        self,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[LikesComplianceResponse, None, None]:
        """
        Stream Likes compliance data (Streaming)
        Streams all compliance data related to Likes for Users.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Likes Compliance events will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the Likes Compliance events will be provided.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            LikesComplianceResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/likes/compliance/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=LikesComplianceResponse,
        )


    def posts(
        self,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsResponse, None, None]:
        """
        Stream filtered Posts (Streaming)
        Streams Posts in real-time matching the active rule set.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/search/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsResponse,
        )


    def posts_firehose_en(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsFirehoseEnResponse, None, None]:
        """
        Stream English Posts (Streaming)
        Streams all public English-language Posts in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsFirehoseEnResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/firehose/stream/lang/en"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsFirehoseEnResponse,
        )


    def labels_compliance(
        self,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[LabelsComplianceResponse, None, None]:
        """
        Stream Post labels (Streaming)
        Streams all labeling events applied to Posts.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Post labels will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the Post labels will be provided.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            LabelsComplianceResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/label/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=LabelsComplianceResponse,
        )


    def users_compliance(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[UsersComplianceResponse, None, None]:
        """
        Stream Users compliance data (Streaming)
        Streams all compliance data related to Users.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the User Compliance events will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the User Compliance events will be provided.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            UsersComplianceResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/users/compliance/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=UsersComplianceResponse,
        )


    def posts_firehose_ko(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsFirehoseKoResponse, None, None]:
        """
        Stream Korean Posts (Streaming)
        Streams all public Korean-language Posts in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsFirehoseKoResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/firehose/stream/lang/ko"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsFirehoseKoResponse,
        )


    def posts_sample(
        self,
        backfill_minutes: int = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsSampleResponse, None, None]:
        """
        Stream sampled Posts (Streaming)
        Streams a 1% sample of public Posts in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsSampleResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/sample/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsSampleResponse,
        )


    def posts_compliance(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsComplianceResponse, None, None]:
        """
        Stream Posts compliance data (Streaming)
        Streams all compliance data related to Posts.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Post Compliance events will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Post Compliance events will be provided.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsComplianceResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/compliance/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsComplianceResponse,
        )


    def get_rule_counts(self, rules_count_fields: List = None) -> GetRuleCountsResponse:
        """
        Get stream rule counts
        Retrieves the count of rules in the active rule set for the filtered stream.
        Args:
            rules_count_fields: A comma separated list of RulesCount fields to display.
            Returns:
            GetRuleCountsResponse: Response data
        """
        url = self.client.base_url + "/2/tweets/search/stream/rules/counts"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if rules_count_fields is not None:
            params["rules_count.fields"] = ",".join(
                str(item) for item in rules_count_fields
            )
        headers = {}
        # Prepare request data
        json_data = None
        # Select authentication method based on endpoint requirements and available credentials
        # Priority strategy (matches TypeScript):
        # 1. If endpoint only accepts one method, use that (if available)
        # 2. If endpoint accepts multiple methods:
        #    - For write operations (POST/PUT/DELETE/PATCH): Prefer OAuth1 > OAuth2 User Token > Bearer Token
        #    - For read operations (GET): Prefer Bearer Token > OAuth2 User Token > OAuth1
        # 3. If no security requirements: Bearer Token > OAuth2 User Token > OAuth1
        selected_auth = None
        # Check what auth methods we have available
        available_bearer = bool(self.client.bearer_token)
        available_oauth2 = bool(self.client.access_token)
        available_oauth1 = bool(self.client.auth and self.client.auth.access_token)
        # Count acceptable schemes
        acceptable_schemes = []
        acceptable_schemes.append("BearerToken")
        # If only one scheme is acceptable, use it if available
        if len(acceptable_schemes) == 1:
            scheme = acceptable_schemes[0]
            if scheme == "BearerToken" and available_bearer:
                selected_auth = "bearer_token"
            elif scheme == "OAuth2UserToken" and available_oauth2:
                selected_auth = "oauth2_user_context"
            elif scheme == "UserToken" and available_oauth1:
                selected_auth = "oauth1"
        # Multiple schemes acceptable - use priority based on operation type
        elif len(acceptable_schemes) > 1:
            is_write_operation = "get" in ["POST", "PUT", "DELETE", "PATCH"]
            if is_write_operation:
                # Priority for write operations: OAuth1 > OAuth2 User Token > Bearer Token
                if "UserToken" in acceptable_schemes and available_oauth1:
                    selected_auth = "oauth1"
                elif "OAuth2UserToken" in acceptable_schemes and available_oauth2:
                    selected_auth = "oauth2_user_context"
                elif "BearerToken" in acceptable_schemes and available_bearer:
                    selected_auth = "bearer_token"
            else:
                # Priority for read operations: Bearer Token > OAuth2 User Token > OAuth1
                if "BearerToken" in acceptable_schemes and available_bearer:
                    selected_auth = "bearer_token"
                elif "OAuth2UserToken" in acceptable_schemes and available_oauth2:
                    selected_auth = "oauth2_user_context"
                elif "UserToken" in acceptable_schemes and available_oauth1:
                    selected_auth = "oauth1"
        # Apply selected authentication
        if selected_auth == "oauth1":
            # OAuth1 authentication - build proper OAuth1 header dynamically
            # Build OAuth1 header with method, URL, and body
            # For OAuth1, we need to include query params in the URL for signature
            full_url = url
            if params:
                query_string = urllib.parse.urlencode(params)
                full_url = f"{url}?{query_string}" if query_string else url
            # Prepare body for OAuth1 signature (form-encoded, not JSON)
            body_string = ""
            # Build OAuth1 authorization header
            oauth_header = self.client.auth.build_request_header(
                method="get", url=full_url, body=body_string
            )
            headers["Authorization"] = oauth_header
        elif selected_auth == "bearer_token":
            # Bearer token authentication
            if self.client.bearer_token:
                headers["Authorization"] = f"Bearer {self.client.bearer_token}"
            elif self.client.access_token:
                headers["Authorization"] = f"Bearer {self.client.access_token}"
        elif selected_auth == "oauth2_user_context":
            # OAuth2 User Token authentication
            if self.client.access_token:
                headers["Authorization"] = f"Bearer {self.client.access_token}"
                # Check if token needs refresh
                if self.client.oauth2_auth and self.client.token:
                    if self.client.is_token_expired():
                        self.client.refresh_token()
                        if self.client.access_token:
                            headers["Authorization"] = (
                                f"Bearer {self.client.access_token}"
                            )
        # Make the request
        if not selected_auth:
            # No suitable auth method found - validate authentication
            required_schemes = (
                acceptable_schemes if "acceptable_schemes" in locals() else []
            )
            if required_schemes:
                available = []
                if available_bearer and "BearerToken" in required_schemes:
                    available.append("BearerToken")
                if available_oauth2 and "OAuth2UserToken" in required_schemes:
                    available.append("OAuth2UserToken")
                if available_oauth1 and "UserToken" in required_schemes:
                    available.append("UserToken")
                if not available:
                    raise ValueError(
                        f"Authentication required for this endpoint. Required schemes: {required_schemes}. Available: {[s for s in required_schemes if (s == 'BearerToken' and available_bearer) or (s == 'OAuth2UserToken' and available_oauth2) or (s == 'UserToken' and available_oauth1)]}"
                    )
        response = self.client.session.get(
            url,
            params=params,
            headers=headers,
        )
        # Check for errors
        response.raise_for_status()
        # Parse the response data
        response_data = response.json()
        # Convert to Pydantic model if applicable
        return GetRuleCountsResponse.model_validate(response_data)


    def likes_sample10(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        like_with_tweet_author_fields: List = None,
        expansions: List = None,
        user_fields: List = None,
        tweet_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[LikesSample10Response, None, None]:
        """
        Stream sampled Likes (Streaming)
        Streams a 10% sample of public Likes in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Likes will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            like_with_tweet_author_fields: A comma separated list of LikeWithTweetAuthor fields to display.
            expansions: A comma separated list of fields to expand.
            user_fields: A comma separated list of User fields to display.
            tweet_fields: A comma separated list of Tweet fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            LikesSample10Response: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/likes/sample10/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if like_with_tweet_author_fields is not None:
            params["like_with_tweet_author.fields"] = ",".join(
                str(item) for item in like_with_tweet_author_fields
            )
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=LikesSample10Response,
        )


    def get_rules(
        self, ids: List = None, max_results: int = None, pagination_token: str = None
    ) -> Iterator[GetRulesResponse]:
        """
        Get stream rules
        Retrieves the active rule set or a subset of rules for the filtered stream.
        Args:
            ids: A comma-separated list of Rule IDs.
            max_results: The maximum number of results.
            pagination_token: This value is populated by passing the 'next_token' returned in a request to paginate through results.
            Yields:
            GetRulesResponse: One page of results at a time. Automatically handles pagination using next_token.
        Note:
            This method automatically paginates through all results. To get just the first page,
            you can call it once and break, or use the pagination_token parameter to start at a specific page.
        """
        url = self.client.base_url + "/2/tweets/search/stream/rules"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        headers = {}
        # Prepare request data
        json_data = None
        # Determine pagination parameter name
        pagination_param_name = "pagination_token"
        # Start with provided pagination_token, or None for first page
        # Check if pagination_token parameter exists in the method signature
        current_pagination_token = pagination_token
        while True:
            # Build query parameters for this page
            page_params = {}
            if ids is not None:
                page_params["ids"] = ",".join(str(item) for item in ids)
            if max_results is not None:
                page_params["max_results"] = max_results
            # Add pagination token for this page
            if current_pagination_token:
                page_params[pagination_param_name] = current_pagination_token
            # Select authentication method (same logic as regular_request)
            # Priority strategy (matches TypeScript):
            # 1. If endpoint only accepts one method, use that (if available)
            # 2. If endpoint accepts multiple methods:
            #    - For write operations (POST/PUT/DELETE/PATCH): Prefer OAuth1 > OAuth2 User Token > Bearer Token
            #    - For read operations (GET): Prefer Bearer Token > OAuth2 User Token > OAuth1
            # 3. If no security requirements: Bearer Token > OAuth2 User Token > OAuth1
            page_selected_auth = None
            # Check what auth methods we have available
            page_available_bearer = bool(self.client.bearer_token)
            page_available_oauth2 = bool(self.client.access_token)
            page_available_oauth1 = bool(
                self.client.auth and self.client.auth.access_token
            )
            # Count acceptable schemes
            page_acceptable_schemes = []
            page_acceptable_schemes.append("BearerToken")
            # If only one scheme is acceptable, use it if available
            if len(page_acceptable_schemes) == 1:
                scheme = page_acceptable_schemes[0]
                if scheme == "BearerToken" and page_available_bearer:
                    page_selected_auth = "bearer_token"
                elif scheme == "OAuth2UserToken" and page_available_oauth2:
                    page_selected_auth = "oauth2_user_context"
                elif scheme == "UserToken" and page_available_oauth1:
                    page_selected_auth = "oauth1"
            # Multiple schemes acceptable - use priority based on operation type
            elif len(page_acceptable_schemes) > 1:
                is_write_operation = "get" in ["POST", "PUT", "DELETE", "PATCH"]
                if is_write_operation:
                    # Priority for write operations: OAuth1 > OAuth2 User Token > Bearer Token
                    if "UserToken" in page_acceptable_schemes and page_available_oauth1:
                        page_selected_auth = "oauth1"
                    elif (
                        "OAuth2UserToken" in page_acceptable_schemes
                        and page_available_oauth2
                    ):
                        page_selected_auth = "oauth2_user_context"
                    elif (
                        "BearerToken" in page_acceptable_schemes
                        and page_available_bearer
                    ):
                        page_selected_auth = "bearer_token"
                else:
                    # Priority for read operations: Bearer Token > OAuth2 User Token > OAuth1
                    if (
                        "BearerToken" in page_acceptable_schemes
                        and page_available_bearer
                    ):
                        page_selected_auth = "bearer_token"
                    elif (
                        "OAuth2UserToken" in page_acceptable_schemes
                        and page_available_oauth2
                    ):
                        page_selected_auth = "oauth2_user_context"
                    elif (
                        "UserToken" in page_acceptable_schemes and page_available_oauth1
                    ):
                        page_selected_auth = "oauth1"
            # Apply selected authentication for this page
            page_headers = headers.copy()
            if page_selected_auth == "oauth1":
                # OAuth1 authentication - build proper OAuth1 header dynamically
                # Build OAuth1 header with method, URL, and body
                # For OAuth1, we need to include query params in the URL for signature
                full_url = url
                if page_params:
                    query_string = urllib.parse.urlencode(page_params)
                    full_url = f"{url}?{query_string}" if query_string else url
                # Prepare body for OAuth1 signature (form-encoded, not JSON)
                body_string = ""
                # Build OAuth1 authorization header
                oauth_header = self.client.auth.build_request_header(
                    method="get", url=full_url, body=body_string
                )
                page_headers["Authorization"] = oauth_header
            elif page_selected_auth == "bearer_token":
                # Bearer token authentication
                if self.client.bearer_token:
                    page_headers["Authorization"] = f"Bearer {self.client.bearer_token}"
                elif self.client.access_token:
                    page_headers["Authorization"] = f"Bearer {self.client.access_token}"
            elif page_selected_auth == "oauth2_user_context":
                # OAuth2 User Token authentication
                if self.client.access_token:
                    page_headers["Authorization"] = f"Bearer {self.client.access_token}"
                    # Check if token needs refresh
                    if self.client.oauth2_auth and self.client.token:
                        if self.client.is_token_expired():
                            self.client.refresh_token()
                            if self.client.access_token:
                                page_headers["Authorization"] = (
                                    f"Bearer {self.client.access_token}"
                                )
            # Make the request
            if not page_selected_auth:
                # No suitable auth method found - validate authentication
                required_schemes = (
                    page_acceptable_schemes
                    if "page_acceptable_schemes" in locals()
                    else []
                )
                if required_schemes:
                    available = []
                    if page_available_bearer and "BearerToken" in required_schemes:
                        available.append("BearerToken")
                    if page_available_oauth2 and "OAuth2UserToken" in required_schemes:
                        available.append("OAuth2UserToken")
                    if page_available_oauth1 and "UserToken" in required_schemes:
                        available.append("UserToken")
                    if not available:
                        raise ValueError(
                            f"Authentication required for this endpoint. Required schemes: {required_schemes}. Available: {[s for s in required_schemes if (s == 'BearerToken' and page_available_bearer) or (s == 'OAuth2UserToken' and page_available_oauth2) or (s == 'UserToken' and page_available_oauth1)]}"
                        )
            response = self.client.session.get(
                url,
                params=page_params,
                headers=page_headers,
            )
            # Check for errors
            response.raise_for_status()
            # Parse the response data
            response_data = response.json()
            # Convert to Pydantic model if applicable
            page_response = GetRulesResponse.model_validate(response_data)
            # Yield this page
            yield page_response
            # Extract next_token from response
            next_token = None
            try:
                # Try response.meta.next_token (most common pattern)
                if hasattr(page_response, "meta") and page_response.meta is not None:
                    meta = page_response.meta
                    # If meta is a Pydantic model, try to dump it
                    if hasattr(meta, "model_dump"):
                        try:
                            meta_dict = meta.model_dump()
                            next_token = meta_dict.get("next_token")
                        except (AttributeError, TypeError):
                            pass
                    # Otherwise try attribute access
                    if not next_token and hasattr(meta, "next_token"):
                        next_token = getattr(meta, "next_token", None)
                    # If meta is a dict, access it directly
                    if not next_token and isinstance(meta, dict):
                        next_token = meta.get("next_token")
            except (AttributeError, TypeError):
                pass
            # Try dict access if we have a dict
            if not next_token and isinstance(response_data, dict):
                try:
                    meta = response_data.get("meta")
                    if meta and isinstance(meta, dict):
                        next_token = meta.get("next_token")
                except (AttributeError, TypeError, KeyError):
                    pass
            # If no next_token, we're done
            if not next_token:
                break
            # Update token for next iteration
            current_pagination_token = next_token

            # Optional: Add rate limit backoff here if needed
            # time.sleep(0.1)  # Small delay to avoid rate limits


    def update_rules(
        self, body: UpdateRulesRequest, dry_run: bool = None, delete_all: bool = None
    ) -> UpdateRulesResponse:
        """
        Update stream rules
        Adds or deletes rules from the active rule set for the filtered stream.
        Args:
            dry_run: Dry Run can be used with both the add and delete action, with the expected result given, but without actually taking any action in the system (meaning the end state will always be as it was when the request was submitted). This is particularly useful to validate rule changes.
            delete_all: Delete All can be used to delete all of the rules associated this client app, it should be specified with no other parameters. Once deleted, rules cannot be recovered.
            body: Request body
        Returns:
            UpdateRulesResponse: Response data
        """
        url = self.client.base_url + "/2/tweets/search/stream/rules"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if dry_run is not None:
            params["dry_run"] = dry_run
        if delete_all is not None:
            params["delete_all"] = delete_all
        headers = {}
        headers["Content-Type"] = "application/json"
        # Prepare request data
        json_data = None
        if body is not None:
            json_data = (
                body.model_dump(exclude_none=True)
                if hasattr(body, "model_dump")
                else body
            )
        # Select authentication method based on endpoint requirements and available credentials
        # Priority strategy (matches TypeScript):
        # 1. If endpoint only accepts one method, use that (if available)
        # 2. If endpoint accepts multiple methods:
        #    - For write operations (POST/PUT/DELETE/PATCH): Prefer OAuth1 > OAuth2 User Token > Bearer Token
        #    - For read operations (GET): Prefer Bearer Token > OAuth2 User Token > OAuth1
        # 3. If no security requirements: Bearer Token > OAuth2 User Token > OAuth1
        selected_auth = None
        # Check what auth methods we have available
        available_bearer = bool(self.client.bearer_token)
        available_oauth2 = bool(self.client.access_token)
        available_oauth1 = bool(self.client.auth and self.client.auth.access_token)
        # Count acceptable schemes
        acceptable_schemes = []
        acceptable_schemes.append("BearerToken")
        # If only one scheme is acceptable, use it if available
        if len(acceptable_schemes) == 1:
            scheme = acceptable_schemes[0]
            if scheme == "BearerToken" and available_bearer:
                selected_auth = "bearer_token"
            elif scheme == "OAuth2UserToken" and available_oauth2:
                selected_auth = "oauth2_user_context"
            elif scheme == "UserToken" and available_oauth1:
                selected_auth = "oauth1"
        # Multiple schemes acceptable - use priority based on operation type
        elif len(acceptable_schemes) > 1:
            is_write_operation = "post" in ["POST", "PUT", "DELETE", "PATCH"]
            if is_write_operation:
                # Priority for write operations: OAuth1 > OAuth2 User Token > Bearer Token
                if "UserToken" in acceptable_schemes and available_oauth1:
                    selected_auth = "oauth1"
                elif "OAuth2UserToken" in acceptable_schemes and available_oauth2:
                    selected_auth = "oauth2_user_context"
                elif "BearerToken" in acceptable_schemes and available_bearer:
                    selected_auth = "bearer_token"
            else:
                # Priority for read operations: Bearer Token > OAuth2 User Token > OAuth1
                if "BearerToken" in acceptable_schemes and available_bearer:
                    selected_auth = "bearer_token"
                elif "OAuth2UserToken" in acceptable_schemes and available_oauth2:
                    selected_auth = "oauth2_user_context"
                elif "UserToken" in acceptable_schemes and available_oauth1:
                    selected_auth = "oauth1"
        # Apply selected authentication
        if selected_auth == "oauth1":
            # OAuth1 authentication - build proper OAuth1 header dynamically
            # Build OAuth1 header with method, URL, and body
            # For OAuth1, we need to include query params in the URL for signature
            full_url = url
            if params:
                query_string = urllib.parse.urlencode(params)
                full_url = f"{url}?{query_string}" if query_string else url
            # Prepare body for OAuth1 signature (form-encoded, not JSON)
            body_string = ""
            if json_data:
                # OAuth1 spec: JSON bodies are NOT included in signature
                # But we still need to pass the body for the request
                body_string = ""
            # Build OAuth1 authorization header
            oauth_header = self.client.auth.build_request_header(
                method="post", url=full_url, body=body_string
            )
            headers["Authorization"] = oauth_header
        elif selected_auth == "bearer_token":
            # Bearer token authentication
            if self.client.bearer_token:
                headers["Authorization"] = f"Bearer {self.client.bearer_token}"
            elif self.client.access_token:
                headers["Authorization"] = f"Bearer {self.client.access_token}"
        elif selected_auth == "oauth2_user_context":
            # OAuth2 User Token authentication
            if self.client.access_token:
                headers["Authorization"] = f"Bearer {self.client.access_token}"
                # Check if token needs refresh
                if self.client.oauth2_auth and self.client.token:
                    if self.client.is_token_expired():
                        self.client.refresh_token()
                        if self.client.access_token:
                            headers["Authorization"] = (
                                f"Bearer {self.client.access_token}"
                            )
        # Make the request
        if not selected_auth:
            # No suitable auth method found - validate authentication
            required_schemes = (
                acceptable_schemes if "acceptable_schemes" in locals() else []
            )
            if required_schemes:
                available = []
                if available_bearer and "BearerToken" in required_schemes:
                    available.append("BearerToken")
                if available_oauth2 and "OAuth2UserToken" in required_schemes:
                    available.append("OAuth2UserToken")
                if available_oauth1 and "UserToken" in required_schemes:
                    available.append("UserToken")
                if not available:
                    raise ValueError(
                        f"Authentication required for this endpoint. Required schemes: {required_schemes}. Available: {[s for s in required_schemes if (s == 'BearerToken' and available_bearer) or (s == 'OAuth2UserToken' and available_oauth2) or (s == 'UserToken' and available_oauth1)]}"
                    )
        response = self.client.session.post(
            url,
            params=params,
            headers=headers,
            json=json_data,
        )
        # Check for errors
        response.raise_for_status()
        # Parse the response data
        response_data = response.json()
        # Convert to Pydantic model if applicable
        return UpdateRulesResponse.model_validate(response_data)


    def likes_firehose(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        like_with_tweet_author_fields: List = None,
        expansions: List = None,
        user_fields: List = None,
        tweet_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[LikesFirehoseResponse, None, None]:
        """
        Stream all Likes (Streaming)
        Streams all public Likes in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Likes will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            like_with_tweet_author_fields: A comma separated list of LikeWithTweetAuthor fields to display.
            expansions: A comma separated list of fields to expand.
            user_fields: A comma separated list of User fields to display.
            tweet_fields: A comma separated list of Tweet fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            LikesFirehoseResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/likes/firehose/stream"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if like_with_tweet_author_fields is not None:
            params["like_with_tweet_author.fields"] = ",".join(
                str(item) for item in like_with_tweet_author_fields
            )
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=LikesFirehoseResponse,
        )


    def posts_firehose_pt(
        self,
        partition: int,
        backfill_minutes: int = None,
        start_time: str = None,
        end_time: str = None,
        tweet_fields: List = None,
        expansions: List = None,
        media_fields: List = None,
        poll_fields: List = None,
        user_fields: List = None,
        place_fields: List = None,
        stream_config: Optional[StreamConfig] = None,
    ) -> Generator[PostsFirehosePtResponse, None, None]:
        """
        Stream Portuguese Posts (Streaming)
        Streams all public Portuguese-language Posts in real-time.
        This is a streaming endpoint that yields data in real-time as it becomes available.
        Each yielded item represents a single data point from the stream.
        The connection is automatically managed with exponential backoff retry logic.
        If the stream disconnects, the SDK will automatically reconnect without client intervention.
        Args:
            backfill_minutes: The number of minutes of backfill requested.
            partition: The partition number.
            start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided.
            end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided.
            tweet_fields: A comma separated list of Tweet fields to display.
            expansions: A comma separated list of fields to expand.
            media_fields: A comma separated list of Media fields to display.
            poll_fields: A comma separated list of Poll fields to display.
            user_fields: A comma separated list of User fields to display.
            place_fields: A comma separated list of Place fields to display.
            stream_config: Optional StreamConfig for customizing retry behavior, timeouts, and callbacks.
                Configure max_retries (-1 for infinite), initial_backoff, max_backoff, and lifecycle callbacks
                (on_connect, on_disconnect, on_reconnect, on_error) for monitoring connection state.
        Yields:
            PostsFirehosePtResponse: Individual streaming data items
        Raises:
            StreamError: If a non-retryable error occurs (auth errors, client errors) or max retries exceeded.
        """
        url = self.client.base_url + "/2/tweets/firehose/stream/lang/pt"
        # Priority: bearer_token > access_token (matches TypeScript behavior)
        if self.client.bearer_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.bearer_token}"
            )
        elif self.client.access_token:
            self.client.session.headers["Authorization"] = (
                f"Bearer {self.client.access_token}"
            )
        params = {}
        if backfill_minutes is not None:
            params["backfill_minutes"] = backfill_minutes
        if partition is not None:
            params["partition"] = partition
        if start_time is not None:
            params["start_time"] = start_time
        if end_time is not None:
            params["end_time"] = end_time
        if tweet_fields is not None:
            params["tweet.fields"] = ",".join(str(item) for item in tweet_fields)
        if expansions is not None:
            params["expansions"] = ",".join(str(item) for item in expansions)
        if media_fields is not None:
            params["media.fields"] = ",".join(str(item) for item in media_fields)
        if poll_fields is not None:
            params["poll.fields"] = ",".join(str(item) for item in poll_fields)
        if user_fields is not None:
            params["user.fields"] = ",".join(str(item) for item in user_fields)
        if place_fields is not None:
            params["place.fields"] = ",".join(str(item) for item in place_fields)
        headers = {
            "Accept": "application/json",
        }
        # Prepare request data
        json_data = None
        # Ensure params is defined (build_query_params should set it, but initialize if not)
        try:
            _ = params  # Check if params exists
        except NameError:
            params = {}  # Initialize if not defined
        # Use robust streaming with automatic retry and exponential backoff
        yield from stream_with_retry(
            session=self.client.session,
            method="get",
            url=url,
            config=stream_config,
            params=params,
            headers=headers,
            response_model=PostsFirehosePtResponse,
        )
