from typing import Any

from arcade.sdk import ToolContext
from arcade.sdk.errors import ToolExecutionError


def get_tweet_url(tweet_id: str) -> str:
    """Get the URL of a tweet given its ID."""
    return f"https://x.com/x/status/{tweet_id}"


def get_headers_with_token(context: ToolContext) -> dict[str, str]:
    """Get the headers for a request to the X API."""
    if context.authorization is None or context.authorization.token is None:
        raise ToolExecutionError(
            "Missing Token. Authorization is required to post a tweet.",
            developer_message="Token is not set in the ToolContext.",
        )
    token = (
        context.authorization.token if context.authorization and context.authorization.token else ""
    )
    return {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
    }


def parse_search_recent_tweets_response(response_data: dict[str, Any]) -> dict[str, Any]:
    """
    Parses response from the X API search recent tweets endpoint.
    Returns the modified response data with added 'tweet_url', 'author_username', and 'author_name'.
    """
    if not sanity_check_tweets_data(response_data):
        return {"data": [], "next_token": ""}

    # Add 'tweet_url' to each tweet
    for tweet in response_data["data"]:
        tweet["tweet_url"] = get_tweet_url(tweet["id"])

    # Add 'author_username' and 'author_name' to each tweet
    for tweet_data, user_data in zip(
        response_data["data"], response_data["includes"]["users"], strict=False
    ):
        tweet_data["author_username"] = user_data["username"]
        tweet_data["author_name"] = user_data["name"]

    return response_data


def sanity_check_tweets_data(tweets_data: dict[str, Any]) -> bool:
    """
    Sanity check the tweets data.
    Returns True if the tweets data is valid and contains tweets, False otherwise.
    """
    if not tweets_data.get("data"):
        return False
    # prefer clarity over appeasing linter here
    if not tweets_data.get("includes", {}).get("users"):  # noqa: SIM103
        return False
    return True


def expand_long_tweet(tweet_data: dict[str, Any]) -> None:
    """Expand a long tweet.

    For tweets exceeding 280 characters,
    replace the truncated tweet text with the full tweet text.
    """
    if tweet_data.get("note_tweet"):
        tweet_data["text"] = tweet_data["note_tweet"]["text"]
        del tweet_data["note_tweet"]


def expand_urls_in_tweets(
    tweets_data: list[dict[str, Any]], delete_entities: bool = True
) -> list[dict[str, Any]]:
    """
    Returns a new list of tweets with expanded URLs.
    """
    new_tweets = []
    for tweet_data in tweets_data:
        new_tweet = tweet_data.copy()
        if "entities" in new_tweet and "urls" in new_tweet["entities"]:
            for url_entity in new_tweet["entities"]["urls"]:
                short_url = url_entity["url"]
                expanded_url = url_entity["expanded_url"]
                new_tweet["text"] = new_tweet["text"].replace(short_url, expanded_url)

        if delete_entities:
            new_tweet.pop("entities", None)
        new_tweets.append(new_tweet)
    return new_tweets


def expand_urls_in_user_description(user_data: dict, delete_entities: bool = True) -> dict:
    """
    Returns a new user data dict with expanded URLs in the description.
    """
    new_user_data = user_data.copy()
    description_urls = new_user_data.get("entities", {}).get("description", {}).get("urls", [])
    description = new_user_data.get("description", "")
    for url_info in description_urls:
        t_co_link = url_info["url"]
        expanded_url = url_info["expanded_url"]
        description = description.replace(t_co_link, expanded_url)
    new_user_data["description"] = description

    if delete_entities:
        new_user_data.pop("entities", None)
    return new_user_data


def expand_urls_in_user_url(user_data: dict, delete_entities: bool = True) -> dict:
    """
    Returns a new user data dict with expanded URLs in the URL field.
    """
    new_user_data = user_data.copy()
    url_urls = new_user_data.get("entities", {}).get("url", {}).get("urls", [])
    url = new_user_data.get("url", "")
    for url_info in url_urls:
        t_co_link = url_info["url"]
        expanded_url = url_info["expanded_url"]
        url = url.replace(t_co_link, expanded_url)
    new_user_data["url"] = url

    if delete_entities:
        new_user_data.pop("entities", None)
    return new_user_data


def remove_none_values(params: dict) -> dict:
    """
    Remove key/value pairs with None values from a dictionary.

    Args:
        params: The dictionary to clean

    Returns:
        A new dictionary with None values removed
    """
    return {k: v for k, v in params.items() if v is not None}


def expand_attached_media(params: dict) -> dict:
    """
    Include attached media metadata in the request parameters.
    """
    params["expansions"] += ",attachments.media_keys"
    params["tweet.fields"] += ",attachments"
    params["media.fields"] = ",".join([
        # media_key, url and type are returned by default, added here for clarity
        "media_key",
        "url",
        "type",
        "duration_ms",
        "height",
        "width",
        "preview_image_url",
        "alt_text",
        "public_metrics",
    ])
    return params
