package twi

import (
	"fmt"
)

// TweetLikesLookupResponse is the user from the tweet likes
type TweetLikesLookupResponse struct {
	Raw       *UserRaw
	Meta      *TweetLikesMeta `json:"meta"`
	RateLimit *RateLimit
}

// TweetLikesMeta is the meta data from the response
type TweetLikesMeta struct {
	ResultCount   int    `json:"result_count"`
	NextToken     string `json:"next_token"`
	PreviousToken string `json:"previous_token"`
}


// UserRaw is the raw response from the user lookup endpoint
type UserRaw struct {
	Users        []*UserObj       `json:"data"`
	Includes     *UserRawIncludes `json:"includes,omitempty"`
	Errors       []*ErrorObj      `json:"errors,omitempty"`
	dictionaries map[string]*UserDictionary
}

// UserDictionary is a struct of an user and all of the reference objects
type UserDictionary struct {
	User        UserObj
	PinnedTweet *TweetObj
}

// UserObj contains Twitter user account metadata describing the referenced user
type UserObj struct {
	ID              string          `json:"id"`
	Name            string          `json:"name"`
	UserName        string          `json:"username"`
	CreatedAt       string          `json:"created_at,omitempty"`
	Description     string          `json:"description,omitempty"`
	Entities        *EntitiesObj    `json:"entities,omitempty"`
	Location        string          `json:"location,omitempty"`
	PinnedTweetID   string          `json:"pinned_tweet_id,omitempty"`
	ProfileImageURL string          `json:"profile_image_url,omitempty"`
	Protected       bool            `json:"protected,omitempty"`
	PublicMetrics   *UserMetricsObj `json:"public_metrics,omitempty"`
	URL             string          `json:"url,omitempty"`
	Verified        bool            `json:"verified,omitempty"`
	WithHeld        *WithHeldObj    `json:"withheld,omitempty"`
}

// UserMetricsObj contains details about activity for this user
type UserMetricsObj struct {
	Followers int `json:"followers_count"`
	Following int `json:"following_count"`
	Tweets    int `json:"tweet_count"`
	Listed    int `json:"listed_count"`
}

// ErrorObj is part of the partial errors in the response
type ErrorObj struct {
	Title        string      `json:"title"`
	Detail       string      `json:"detail"`
	Type         string      `json:"type"`
	ResourceType string      `json:"resource_type"`
	Parameter    string      `json:"parameter"`
	Value        interface{} `json:"value"`
}

// Error is part of the HTTP response error
type Error struct {
	Parameters interface{} `json:"parameters"`
	Message    string      `json:"message"`
}

// ErrorResponse is returned by a non-success callout
type ErrorResponse struct {
	StatusCode int
	Errors     []Error    `json:"errors"`
	Title      string     `json:"title"`
	Detail     string     `json:"detail"`
	Type       string     `json:"type"`
	RateLimit  *RateLimit `json:"-"`
}

func (e ErrorResponse) Error() string {
	return fmt.Sprintf("twitter callout status %d %s:%s", e.StatusCode, e.Title, e.Detail)
}
// RateLimit are the rate limit values from the response header
type RateLimit struct {
	Limit     int
	Remaining int
	Reset     Epoch
}

// Epoch is the UNIX seconds from 1/1/1970
type Epoch int

// UserRawIncludes contains any additional information from the user callout
type UserRawIncludes struct {
	Tweets       []*TweetObj `json:"tweets,omitempty"`
	pinnedTweets map[string]*TweetObj
}

// TweetObj is the primary object on the tweets endpoints
type TweetObj struct {
	ID                 string                       `json:"id"`
	Text               string                       `json:"text"`
	Attachments        *TweetAttachmentsObj         `json:"attachments,omitempty"`
	AuthorID           string                       `json:"author_id,omitempty"`
	ContextAnnotations []*TweetContextAnnotationObj `json:"context_annotations,omitempty"`
	ConversationID     string                       `json:"conversation_id,omitempty"`
	CreatedAt          string                       `json:"created_at,omitempty"`
	Entities           *EntitiesObj                 `json:"entities,omitempty"`
	Geo                *TweetGeoObj                 `json:"geo,omitempty"`
	InReplyToUserID    string                       `json:"in_reply_to_user_id,omitempty"`
	Language           string                       `json:"lang,omitempty"`
	NonPublicMetrics   *TweetMetricsObj             `json:"non_public_metrics,omitempty"`
	OrganicMetrics     *TweetMetricsObj             `json:"organic_metrics,omitempty"`
	PossiblySensitive  bool                         `json:"possibly_sensitive,omitempty"`
	PromotedMetrics    *TweetMetricsObj             `json:"promoted_metrics,omitempty"`
	PublicMetrics      *TweetMetricsObj             `json:"public_metrics,omitempty"`
	ReferencedTweets   []*TweetReferencedTweetObj   `json:"referenced_tweets,omitempty"`
	Source             string                       `json:"source,omitempty"`
	WithHeld           *WithHeldObj                 `json:"withheld,omitempty"`
}

// TweetAttachmentsObj specifics the type of attachment present in the tweet
type TweetAttachmentsObj struct {
	MediaKeys []string `json:"media_keys"`
	PollIDs   []string `json:"poll_ids"`
}

// TweetContextAnnotationObj contain the context annotation
type TweetContextAnnotationObj struct {
	Domain TweetContextObj `json:"domain"`
	Entity TweetContextObj `json:"entity"`
}

// TweetContextObj contains the elements which identify detailed information regarding the domain classification based on the Tweet text
type TweetContextObj struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
}

// TweetGeoObj contains details about the location tagged by the user in this Tweet, if they specified one.
type TweetGeoObj struct {
	PlaceID     string                 `json:"place_id"`
	Coordinates TweetGeoCoordinatesObj `json:"coordinates"`
}

// TweetGeoCoordinatesObj contains details about the coordinates of the location tagged by the user in this Tweet, if they specified one.
type TweetGeoCoordinatesObj struct {
	Type        string    `json:"type"`
	Coordinates []float64 `json:"coordinates"`
}

// TweetMetricsObj engagement metrics for the Tweet at the time of the request
type TweetMetricsObj struct {
	Impressions       int `json:"impression_count"`
	URLLinkClicks     int `json:"url_link_clicks"`
	UserProfileClicks int `json:"user_profile_clicks"`
	Likes             int `json:"like_count"`
	Replies           int `json:"reply_count"`
	Retweets          int `json:"retweet_count"`
	Quotes            int `json:"quote_count"`
}

// TweetReferencedTweetObj is a Tweet this Tweet refers to
type TweetReferencedTweetObj struct {
	Type string `json:"type"`
	ID   string `json:"id"`
}

// EntitiesObj contains details about text that has a special meaning.
type EntitiesObj struct {
	Annotations []EntityAnnotationObj `json:"annotations"`
	URLs        []EntityURLObj        `json:"urls"`
	HashTags    []EntityTagObj        `json:"hashtags"`
	Mentions    []EntityMentionObj    `json:"mentions"`
	CashTags    []EntityTagObj        `json:"cashtags"`
}

// EntityObj contains the start and end positions of the text
type EntityObj struct {
	Start int `json:"start"`
	End   int `json:"end"`
}

// EntityAnnotationObj contains details about annotations relative to the text.
type EntityAnnotationObj struct {
	EntityObj
	Probability    float64 `json:"probability"`
	Type           string  `json:"type"`
	NormalizedText string  `json:"normalized_text"`
}

// EntityURLObj contains details about text recognized as a URL.
type EntityURLObj struct {
	EntityObj
	URL         string              `json:"url"`
	ExpandedURL string              `json:"expanded_url"`
	DisplayURL  string              `json:"display_url"`
	Status      int                 `json:"status"`
	Title       string              `json:"title"`
	Description string              `json:"description"`
	UnwoundURL  string              `json:"unwound_url"`
	MediaKey    string              `json:"media_key"`
	Images      []EntityURLImageObj `json:"images"`
}

// EntityURLImageObj defines the array of images that are associated with the entity URL object
type EntityURLImageObj struct {
	URL    string `json:"url"`
	Width  int    `json:"width"`
	Height int    `json:"height"`
}

// EntityTagObj contains details about text recognized as a tag
type EntityTagObj struct {
	EntityObj
	Tag string `json:"tag"`
}

// EntityMentionObj contains details about text recognized as a user mention.
type EntityMentionObj struct {
	EntityObj
	UserName string `json:"username"`
}

// WithHeldObj contains withholding details
type WithHeldObj struct {
	Copyright    bool     `json:"copyright"`
	CountryCodes []string `json:"country_codes"`
}

// UserRetweetMeta is the meta data returned by the retweet user lookup
type UserRetweetMeta struct {
	ResultCount   int    `json:"result_count"`
	NextToken     string `json:"next_token"`
	PreviousToken string `json:"previous_token"`
}

// TweetRaw is the raw response from the tweet lookup endpoint
type TweetRaw struct {
	Tweets       []*TweetObj       `json:"data"`
	Includes     *TweetRawIncludes `json:"includes,omitempty"`
	Errors       []*ErrorObj       `json:"errors,omitempty"`
	dictionaries map[string]*TweetDictionary
}

// TweetRawIncludes contains any additional information from the tweet callout
type TweetRawIncludes struct {
	Tweets          []*TweetObj `json:"tweets,omitempty"`
	Users           []*UserObj  `json:"users,omitempty"`
	Places          []*PlaceObj `json:"places,omitempty"`
	Media           []*MediaObj `json:"media,omitempty"`
	Polls           []*PollObj  `json:"polls,omitempty"`
	userIDs         map[string]*UserObj
	userNames       map[string]*UserObj
	pollIDs         map[string]*PollObj
	mediaKeys       map[string]*MediaObj
	placeIDs        map[string]*PlaceObj
	referenceTweets map[string]*TweetObj
}

// QuoteTweetsLookupMeta is the meta data from the response
type QuoteTweetsLookupMeta struct {
	ResultCount int    `json:"result_count"`
	NextToken   string `json:"next_token"`
}

// TweetDictionary is a struct of a tweet and all of the reference objects
type TweetDictionary struct {
	Tweet            TweetObj
	Author           *UserObj
	InReplyUser      *UserObj
	Place            *PlaceObj
	AttachmentPolls  []*PollObj
	AttachmentMedia  []*MediaObj
	Mentions         []*TweetMention
	ReferencedTweets []*TweetReference
}

// TweetMention is the mention and the user associated with it
type TweetMention struct {
	Mention *EntityMentionObj
	User    *UserObj
}

// TweetReference is the tweet referenced and it's dictionary
type TweetReference struct {
	Reference       *TweetReferencedTweetObj
	TweetDictionary *TweetDictionary
}

// PlaceObj tagged in a Tweet is not a primary object on any endpoint
type PlaceObj struct {
	FullName        string       `json:"full_name,omitempty"`
	ID              string       `json:"id"`
	ContainedWithin []string     `json:"contained_within,omitempty"`
	Country         string       `json:"country,omitempty"`
	CountryCode     string       `json:"country_code,omitempty"`
	Geo             *PlaceGeoObj `json:"geo,omitempty"`
	Name            string       `json:"name"`
	PlaceType       string       `json:"place_type,omitempty"`
}

// PlaceGeoObj contains place details
type PlaceGeoObj struct {
	Type       string                 `json:"type"`
	BBox       []float64              `json:"bbox"`
	Properties map[string]interface{} `json:"properties"`
}

// PollObj included in a Tweet is not a primary object on any endpoint
type PollObj struct {
	ID              string           `json:"id"`
	Options         []*PollOptionObj `json:"options,omitempty"`
	DurationMinutes int              `json:"duration_minutes,omitempty"`
	EndDateTime     string           `json:"end_datetime,omitempty"`
	VotingStatus    string           `json:"voting_status,omitempty"`
}

// PollOptionObj contains objects describing each choice in the referenced poll.
type PollOptionObj struct {
	Position int    `json:"position"`
	Label    string `json:"label"`
	Votes    int    `json:"votes"`
}

// MediaObj refers to any image, GIF, or video attached to a Tweet
type MediaObj struct {
	Key              string             `json:"media_key"`
	Type             string             `json:"type"`
	URL              string             `json:"url"`
	DurationMS       int                `json:"duration_ms"`
	Height           int                `json:"height,omitempty"`
	NonPublicMetrics *MediaMetricsObj   `json:"non_public_metrics,omitempty"`
	OrganicMetrics   *MediaMetricsObj   `json:"organic_metrics,omitempty"`
	PreviewImageURL  string             `json:"preview_image_url,omitempty"`
	PromotedMetrics  *MediaMetricsObj   `json:"promoted_metrics,omitempty"`
	PublicMetrics    *MediaMetricsObj   `json:"public_metrics,omitempty"`
	Width            int                `json:"width,omitempty"`
	AltText          string             `json:"alt_text,omitempty"`
	Variants         []*MediaVariantObj `json:"variants,omitempty"`
}

// MediaMetricsObj engagement metrics for the media content at the time of the request
type MediaMetricsObj struct {
	Playback0   int `json:"playback_0_count"`
	Playback100 int `json:"playback_100_count"`
	Playback25  int `json:"playback_25_count"`
	Playback50  int `json:"playback_50_count"`
	Playback75  int `json:"playback_75_count"`
	Views       int `json:"view_count"`
}

// MediaVariantObj the media object display or playback variants with resolution and format
type MediaVariantObj struct {
	BitRate     int    `json:"bit_rate"`
	ContentType string `json:"content_type"`
	URL         string `json:"url"`
}