"""
Twitter API Data Source using Twitter-API.io

This module provides real-time Twitter data collection for sentiment analysis
in trading algorithms. It uses Twitter-API.io for simplified API access.
"""

import asyncio
import os
from dataclasses import dataclass
from datetime import datetime
from typing import Any

import aiohttp

from .base import DataSource


@dataclass
class TwitterConfig:
    """Configuration for Twitter data collection."""

    api_key: str
    query: str = ""
    max_results: int = 100
    tweet_fields: list[str] | None = None
    user_fields: list[str] | None = None
    poll_interval: float = 30.0

    def __post_init__(self):
        if self.tweet_fields is None:
            self.tweet_fields = [
                "created_at",
                "author_id",
                "public_metrics",
                "context_annotations",
                "lang",
                "conversation_id",
            ]
        if self.user_fields is None:
            self.user_fields = ["username", "verified", "public_metrics"]


class TwitterAPISource(DataSource):
    """
    Twitter data source using Twitter-API.io service.

    Provides real-time Twitter data collection with built-in rate limiting
    and error handling for sentiment analysis in trading algorithms.

    Bug Fix #1: Corrected base URL domain from twitter-api.io to api.twitterapi.io
    Note: The exact endpoint may vary - this should be verified with twitterapi.io documentation
    """

    # Bug Fix #1: Corrected domain (was https://twitter-api.io/api/v2)
    BASE_URL = "https://api.twitterapi.io/v2"

    def __init__(self, config: TwitterConfig):
        super().__init__(name="twitter_api", config=config.__dict__)
        self.config = config
        self.session: aiohttp.ClientSession | None = None
        self._running = False

    async def connect(self) -> None:
        """Establish connection to Twitter API."""
        if not self.session:
            # Bug Fix #1: Updated authentication to use x-api-key header format
            # This may need to be Bearer token depending on twitterapi.io requirements
            headers = {"x-api-key": self.config.api_key, "Content-Type": "application/json"}
            self.session = aiohttp.ClientSession(headers=headers)
        self._connected = True

    async def disconnect(self) -> None:
        """Close Twitter API connection."""
        if self.session:
            await self.session.close()
            self.session = None
        self._connected = False
        self._running = False

    async def search_tweets(self, query: str, max_results: int = 100) -> dict[str, Any]:
        """
        Search for tweets matching the query.

        Args:
            query: Twitter search query
            max_results: Maximum number of tweets to return

        Returns:
            Twitter API response with tweet data
        """
        if not self.session:
            raise RuntimeError("Not connected to Twitter API")

        params = {
            "query": query,
            "max_results": min(max_results, 100),
            "tweet.fields": ",".join(self.config.tweet_fields or []),
            "user.fields": ",".join(self.config.user_fields or []),
            "expansions": "author_id",
        }

        # Bug Fix #1: Endpoint path may need adjustment based on twitterapi.io API structure
        # Original: /tweets/search/recent - verify with API documentation
        async with self.session.get(
            f"{self.BASE_URL}/tweets/search/recent", params=params
        ) as response:
            if response.status == 200:
                return await response.json()
            elif response.status == 404:
                # Bug Fix #1: Provide helpful error for 404 (endpoint not found)
                raise RuntimeError(
                    f"Twitter API endpoint not found (404). "
                    f"Please verify the correct endpoint path with twitterapi.io documentation. "
                    f"Attempted: {self.BASE_URL}/tweets/search/recent"
                )
            else:
                error_text = await response.text()
                raise RuntimeError(f"Twitter API error {response.status}: {error_text}")

    async def get_game_tweets(
        self, teams: list[str], hashtags: list[str] | None = None
    ) -> dict[str, Any]:
        """
        Get tweets related to a specific game.

        Args:
            teams: List of team names/hashtags
            hashtags: Additional hashtags to include

        Returns:
            Filtered tweets related to the game
        """
        # Build query for game-specific tweets
        team_terms = [f'"{team}"' for team in teams]
        hashtag_terms = [f"#{tag}" for tag in (hashtags or [])]

        query_parts = []
        if team_terms:
            query_parts.append(f"({' OR '.join(team_terms)})")
        if hashtag_terms:
            query_parts.append(f"({' OR '.join(hashtag_terms)})")

        query = " AND ".join(query_parts)

        # Add filters for quality and recency
        query += " -is:retweet lang:en"

        return await self.search_tweets(query, self.config.max_results)

    async def collect(self) -> Any:
        """
        Continuously collect Twitter data.

        Yields:
            Processed tweet data with metadata
        """
        if not self._connected:
            await self.connect()

        self._running = True

        while self._running:
            try:
                # Use the configured query or default to general sports content
                query = self.config.query or "NFL OR NBA OR MLB -is:retweet lang:en"

                tweets_data = await self.search_tweets(query, self.config.max_results)

                # Process and yield tweets
                if "data" in tweets_data:
                    for tweet in tweets_data["data"]:
                        processed_tweet = self._process_tweet(
                            tweet, tweets_data.get("includes", {})
                        )
                        yield processed_tweet

                # Wait before next poll
                await asyncio.sleep(self.config.poll_interval)

            except Exception as e:
                print(f"Error collecting Twitter data: {e}")
                await asyncio.sleep(60)  # Wait longer on error

    def _process_tweet(self, tweet: dict[str, Any], includes: dict[str, Any]) -> dict[str, Any]:
        """
        Process raw tweet data into structured format.

        Args:
            tweet: Raw tweet data from API
            includes: Additional data (users, media, etc.)

        Returns:
            Processed tweet with metadata
        """
        # Get author information
        author_id = tweet.get("author_id")
        author_info = {}

        if "users" in includes:
            for user in includes["users"]:
                if user["id"] == author_id:
                    author_info = {
                        "username": user.get("username"),
                        "verified": user.get("verified", False),
                        "followers": user.get("public_metrics", {}).get("followers_count", 0),
                    }
                    break

        # Extract metrics
        metrics = tweet.get("public_metrics", {})

        # Process datetime
        created_at = tweet.get("created_at")
        if created_at:
            created_at = datetime.fromisoformat(created_at.replace("Z", "+00:00"))

        return {
            "id": tweet["id"],
            "text": tweet["text"],
            "created_at": created_at,
            "author_id": author_id,
            "author_info": author_info,
            "metrics": {
                "retweet_count": metrics.get("retweet_count", 0),
                "like_count": metrics.get("like_count", 0),
                "reply_count": metrics.get("reply_count", 0),
                "quote_count": metrics.get("quote_count", 0),
            },
            "lang": tweet.get("lang", "en"),
            "context_annotations": tweet.get("context_annotations", []),
            "conversation_id": tweet.get("conversation_id"),
            "source": "twitter",
            "timestamp": datetime.now(),
        }


class GameTwitterSource(TwitterAPISource):
    """
    Specialized Twitter source for specific games.

    Focuses on collecting tweets related to a specific game or teams.
    """

    def __init__(
        self,
        api_key: str,
        teams: list[str],
        hashtags: list[str] | None = None,
        poll_interval: float = 15.0,
    ):
        # Build game-specific query
        team_terms = [f'"{team}"' for team in teams]
        hashtag_terms = [f"#{tag}" for tag in (hashtags or [])]

        query_parts = []
        if team_terms:
            query_parts.append(f"({' OR '.join(team_terms)})")
        if hashtag_terms:
            query_parts.append(f"({' OR '.join(hashtag_terms)})")

        query = " AND ".join(query_parts) if query_parts else " OR ".join(teams)
        query += " -is:retweet lang:en"

        config = TwitterConfig(
            api_key=api_key,
            query=query,
            poll_interval=poll_interval,
            max_results=50,  # More focused, so fewer results needed
        )

        super().__init__(config)
        self.teams = teams
        self.hashtags = hashtags or []
        self.name = f"twitter_game_{' '.join(teams).replace(' ', '_').lower()}"


# Factory function for easy setup
def create_twitter_source(
    api_key: str | None = None,
    teams: list[str] | None = None,
    hashtags: list[str] | None = None,
    query: str | None = None,
    poll_interval: float = 30.0,
) -> TwitterAPISource:
    """
    Create a Twitter data source with sensible defaults.

    Args:
        api_key: Twitter API key (uses TWITTER_API_KEY env var if not provided)
        teams: List of teams to track
        hashtags: List of hashtags to track
        query: Custom search query
        poll_interval: How often to poll for new tweets

    Returns:
        Configured TwitterAPISource
    """
    if api_key is None:
        api_key = os.getenv("TWITTER_API_KEY")
        if not api_key:
            raise ValueError(
                "Twitter API key required. Set TWITTER_API_KEY env var or pass api_key parameter"
            )

    if teams:
        return GameTwitterSource(
            api_key=api_key, teams=teams, hashtags=hashtags, poll_interval=poll_interval
        )
    else:
        config = TwitterConfig(
            api_key=api_key,
            query=query or "NFL OR NBA OR MLB -is:retweet lang:en",
            poll_interval=poll_interval,
        )
        return TwitterAPISource(config)


# Example usage patterns
if __name__ == "__main__":

    async def example():
        # Example 1: Track specific game
        ravens_lions_source = create_twitter_source(
            teams=["Baltimore Ravens", "Detroit Lions"],
            hashtags=["RavensVsLions", "NFL"],
            poll_interval=15.0,
        )

        # Example 2: General sports sentiment
        create_twitter_source(query="NFL OR NBA -is:retweet lang:en", poll_interval=60.0)

        async with ravens_lions_source:
            async for tweet in ravens_lions_source.collect():
                print(f"Tweet: {tweet['text'][:100]}...")
                print(f"Engagement: {tweet['metrics']['like_count']} likes")
                break

    asyncio.run(example())
