from typing import List, Dict, Union, Optional
from dataclasses import dataclass, asdict

Date = str

Url = str
ContentType = str

TweetId = str
UserId = str

MediaKey = str

Error = Dict

@dataclass
class UserPublicMetrics:
    tweet_count: Optional[int] = None
    followers_count: Optional[int] = None
    following_count: Optional[int] = None
    listed_count: Optional[int] = None


@dataclass
class User:
    id: UserId
    
    username: Optional[str] = None
    created_at: Optional[Date] = None
    name: Optional[str] = None
    verified: Optional[bool] = None
    protected: Optional[bool] = None
    profile_image_url: Optional[Url] = None
    description: Optional[str] = None
    url: Optional[str] = None
    location: Optional[str] = None
    
    pinned_tweet_id: Optional[TweetId] = None
    
    public_metrics: Optional[UserPublicMetrics] = None

@dataclass
class PublicMetrics:
    retweet_count: Optional[int] = None
    reply_count: Optional[int] = None
    like_count: Optional[int] = None
    quote_count: Optional[int] = None
    impression_count: Optional[int] = None
    bookmark_count: Optional[int] = None
    
    # on video media (only?)
    view_count: Optional[int] = None

@dataclass
class NonPublicMetrics:
    impression_count: Optional[int] = None
    profile_clicks: Optional[int] = None
    url_link_clicks: Optional[int] = None
    user_profile_clicks: Optional[int] = None
    
@dataclass
class Media:
    media_key: MediaKey
    type: str
    width: int
    height: int
    
@dataclass
class PhotoMedia(Media):
    url: Url
    

@dataclass
class VideoMediaVariant:
    content_type: ContentType
    url: Url
    bit_rate: Optional[int] = None

@dataclass
class VideoMedia (Media):
    preview_image_url: Url
    duration_ms: int
    variants: List[VideoMediaVariant]
    public_metrics: Optional[PublicMetrics] = None
    non_public_metrics: Optional[NonPublicMetrics] = None

@dataclass
class GifMedia (Media):
    preview_image_url: Url
    variants: List[VideoMediaVariant]
    public_metrics: Optional[PublicMetrics] = None
    non_public_metrics: Optional[NonPublicMetrics] = None

@dataclass
class TweetAttachments:
    media_keys: Optional[List[MediaKey]]

@dataclass
class MentionEntity:
    start: int
    end: int
    id: UserId
    username: str

@dataclass
class UrlEntityImage:
    url: Url
    width: int
    height: int

@dataclass
class UrlEntity:
    start: int
    end: int
    url: Url
    expanded_url: Optional[Url] = None
    display_url: Optional[str] = None
    media_key: Optional[MediaKey] = None
    description: Optional[str] = None
    title: Optional[str] = None
    unwound_url: Optional[Url] = None
    status: Optional[int] = None
    images: Optional[List[UrlEntityImage]] = None

@dataclass
class TweetEntities:
    urls: Optional[List[UrlEntity]] = None
    mentions: Optional[List[MentionEntity]] = None

@dataclass 
class ReferencedTweet:
    id: TweetId
    type: str

@dataclass
class Tweet:
    id: TweetId
    created_at: Date
    text: str
    
    conversation_id: Optional[TweetId] = None
    in_reply_to_tweet_id: Optional[TweetId] = None
    author_id: Optional[UserId] = None
    referenced_tweets: Optional[List[ReferencedTweet]] = None
    entities: Optional[TweetEntities] = None
    public_metrics: Optional[PublicMetrics] = None
    non_public_metrics: Optional[NonPublicMetrics] = None
    attachments: Optional[TweetAttachments] = None
    edit_history_tweet_ids: Optional[List[TweetId]] = None


@dataclass
class TweetExpansions:
    users: Optional[List[User]] = None
    media: Optional[List[Union[VideoMedia, PhotoMedia, GifMedia]]] = None
    tweets: Optional[List[Tweet]] = None

@dataclass
class TweetSearchMeta:
    count: Optional[int] = None # is this an error? different endpoint type?
    result_count: Optional[int] = None
    next_token: Optional[str] = None
    newest_id: Optional[TweetId] = None
    oldest_id: Optional[TweetId] = None

@dataclass
class TweetSearchResponse:
    data: Optional[List[Tweet]] = None
    meta: Optional[TweetSearchMeta] = None
    includes: Optional[TweetExpansions] = None
    errors: Optional[List[Error]] = None

@dataclass
class DMEvent:
    id: str
    created_at: str
    event_type: str
    dm_conversation_id: str
    

@dataclass
class DMCMessageCreate(DMEvent):
    sender_id: UserId
    text: str
    referenced_tweet_id: Optional[TweetId] = None
    attachments: Optional[TweetAttachments] = None

@dataclass
class DMParticpantsJoin (DMEvent):
    participant_ids: List[UserId]
    

@dataclass
class DMParticpantsLeave (DMEvent):
    participant_ids: List[UserId]

@dataclass
class DMEventsResponse:
    data: List[Union[DMCMessageCreate, DMParticpantsJoin, DMParticpantsLeave]]
    meta: Optional[TweetSearchMeta] = None # is SearchResultsMeta a general response format?
    includes: Optional[TweetExpansions] = None




UserSearchMeta = Dict


@dataclass
class UserSearchResponse:
    data: Optional[List[User]] = None
    meta: Optional[UserSearchMeta] = None
    includes: Optional[TweetExpansions] = None
    errors: Optional[List[Error]] = None
