package api

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

const (
	maxBrowseLimit  = 100
	maxBrowseOffset = 10000
)

// GetModRequest is the body for the POST /v1/mods/get endpoint
type GetModRequest struct {
	ChannelID string `json:"channel_id"`
	UserID    string `json:"user_id"`
}

// GetModResponse is the response from the POST /v1/mods/get endpoint
type GetModResponse struct {
	IsMod bool `json:"is_mod"`
}

// ListModsRequest is the body for the POST /v1/mods/list endpoint
type ListModsRequest struct {
	ChannelID string `json:"channel_id"`
}

// ListModsResponse is the response from the POST /v1/mods/list endpoint
type ListModsResponse struct {
	Mods []string `json:"mods"`
}

// ListModsV2Request is the body for the POST /v2/mods/list endpoint
type ListModsV2Request struct {
	ChannelID string `json:"channel_id"`
	Cursor    string `json:"cursor"`
	Limit     int    `json:"limit"`
}

// ListModsV2Response is the response from the POST /v2/mods/list endpoint
type ListModsV2Response struct {
	ModResults []ModResult `json:"mod_results"`
}

// ModResult an element in a paginated list of moderators
type ModResult struct {
	Mod    Mod    `json:"mod"`
	Cursor string `json:"cursor"`
}

// Mod is a moderator in a list of mods
type Mod struct {
	UserID string `json:"user_id"`
}

// AddModRequest is the body for the POST /v1/mods/add endpoint
type AddModRequest struct {
	ChannelID        string `json:"channel_id"`
	TargetUserID     string `json:"target_user_id"`
	RequestingUserID string `json:"requesting_user_id"`
}

// RemoveModRequest is the body for the POST /v1/mods/remove endpoint
type RemoveModRequest struct {
	ChannelID        string `json:"channel_id"`
	TargetUserID     string `json:"target_user_id"`
	RequestingUserID string `json:"requesting_user_id"`
}

// ListGameFollowsRequest is the body for the POST /v1/game_follows/list and POST /v1/game_follows/list_live endpoints
type ListGameFollowsRequest struct {
	UserID       string `json:"user_id"`
	Offset       int    `json:"offset"`
	Limit        int    `json:"limit"`
	Cursor       string `json:"cursor"`
	ShowOnlyLive bool   `json:"show_only_live"`

	parsedCursor *int
}

func (req ListGameFollowsRequest) SetParsedCursor(parsedCursor int) {
	req.parsedCursor = &parsedCursor
}

func (req ListGameFollowsRequest) GetParsedCursor() int {
	return *req.parsedCursor
}

// ListGameFollowsResponse is the response from the POST /v1/game_follows/list endpoint
type ListGameFollowsResponse struct {
	Games  []GameFollow `json:"games"`
	Cursor string       `json:"cursor"`
}

// CountGameFollowsRequest is the body for the POST /v1/game_follows/count endpoint
type CountGameFollowsRequest struct {
	UserID       string `json:"user_id"`
	ShowOnlyLive bool   `json:"show_only_live"`
}

// CountGameFollowsResponse is the response from the POST /v1/game_follows/count endpoint
type CountGameFollowsResponse struct {
	Total int `json:"total"`
}

// GameFollowRequest is the body for the POST /v1/game_follows/get, /v1/game_follows/create, and /v1/game_follows/delete endpoints
type GameFollowRequest struct {
	UserID string `json:"user_id"`
	GameID string `json:"game_id"`
}

// GameFollowResponse is the response from the POST /v1/game_follows/get, /v1/game_follows/create, and /v1/game_follows/delete endpoints
type GameFollowResponse struct {
	Game *GameFollow `json:"game"`
}

// GameFollow is the inner struct of the POST /v1/game_follows/list endpoint
type GameFollow struct {
	UserID    string    `json:"user_id"`
	GameID    string    `json:"game_id"`
	CreatedAt time.Time `json:"created_at"`
}

// CountGameFollowersRequest is the body for the POST /v1/game_followers/count endpoint
type CountGameFollowersRequest struct {
	GameID string `json:"game_id"`
}

// CountGameFollowersResponse is the response from the POST /v1/game_followers/count endpoint
type CountGameFollowersResponse struct {
	Total int `json:"total"`
}

// TopCommunitiesRequest is the body for the POST /v1/communities/top endpoint
type TopCommunitiesRequest struct {
	Limit        int    `json:"limit"`
	Cursor       string `json:"cursor"`
	FeaturedOnly bool   `json:"featured_only"`
	Language     string `json:"language"`
	MagicSort    bool   `json:"magic_sort"`
}

// TopCommunitiesResponse is the response from the POST /v1/communities/top endpoint
type TopCommunitiesResponse struct {
	Total   int                  `json:"total"`
	Cursor  string               `json:"cursor"`
	Results []TopCommunityResult `json:"results"`
}

// TopCommunityResult is the inner struct of the POST /v1/communities/top endpoint
type TopCommunityResult struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	DisplayName    string `json:"display_name"`
	Type           string `json:"type"`
	Viewers        int    `json:"viewers"`
	Channels       int    `json:"channels"`
	AvatarImageURL string `json:"avatar_image_url"`
}

// ListFeaturedCommunitiesRequest is the body for the POST /v1/communities/featured/list endpoint
type ListFeaturedCommunitiesRequest struct{}

// ListFeaturedCommunitiesResponse is the response from the POST /v1/communities/featured/list endpoint
type ListFeaturedCommunitiesResponse struct {
	FeaturedCommunities []Community `json:"featured_communities"`
}

// Community is an inner struct representing a community
type Community struct {
	CommunityID         string `json:"community_id"`
	Name                string `json:"name"`
	DisplayName         string `json:"display_name"`
	OwnerUserID         string `json:"owner_user_id"`
	Type                string `json:"type"`
	ShortDescription    string `json:"short_description"`
	LongDescription     string `json:"long_description"`
	LongDescriptionHTML string `json:"long_description_html"`
	Rules               string `json:"rules"`
	RulesHTML           string `json:"rules_html"`
	Email               string `json:"email"`
	Language            string `json:"language"`
	BannerImageURL      string `json:"banner_image_url"`
	BannerImageTemplate string `json:"banner_image_template"`
	AvatarImageURL      string `json:"avatar_image_url"`
	AvatarImageTemplate string `json:"avatar_image_template"`
}

const (
	// Official community types
	CommunityTypeGame     = "game"
	CommunityTypeCreative = "creative"
	CommunityTypeOfficial = "other-official"
	// Unofficial (user-created) community types
	CommunityTypeOther = "other"

	// Character used to separate the values for the Filter fiedl in BrowseTop
	BrowseTopFilterSeparator = ":"
	// Values allowed for the Filter list
	BrowseTopFilterAll         = "all"
	BrowseTopFilterGames       = "games"
	BrowseTopFilterCommunities = "communities"
	BrowseTopFilterCreative    = "creative"
)

// AddFeaturedCommunityRequest is the body for the POST /v1/communities/featured/add endpoint
type AddFeaturedCommunityRequest struct {
	CommunityID string `json:"community_id"`
}

// RemoveFeaturedCommunityRequest is the body for the POST /v1/communities/featured/remove endpoint
type RemoveFeaturedCommunityRequest struct {
	CommunityID string `json:"community_id"`
}

// GetCommunityByNameRequest is the body for the POST /v1/communities/get_by_name endpoint
type GetCommunityByNameRequest struct {
	Name string `json:"name"`
}

// GetCommunityByNameResponse is the response for the POST /v1/communities/get_by_name endpoint
type GetCommunityByNameResponse struct {
	CommunityID string `json:"community_id"`
}

// GetCommunitySettingsRequest is the body for the POST /v1/communities/settings/get endpoint
type GetCommunitySettingsRequest struct {
	CommunityID string `json:"community_id"`
}

// GetCommunitySettingsResponse is the response from the POST /v1/communities/settings/get endpoint
type GetCommunitySettingsResponse struct {
	CommunityID         string `json:"community_id"`
	Name                string `json:"name"`
	DisplayName         string `json:"display_name"`
	OwnerUserID         string `json:"owner_user_id"`
	Type                string `json:"type"`
	ShortDescription    string `json:"short_description"`
	LongDescription     string `json:"long_description"`
	LongDescriptionHTML string `json:"long_description_html"`
	Rules               string `json:"rules"`
	RulesHTML           string `json:"rules_html"`
	Email               string `json:"email"`
	Language            string `json:"language"`
	BannerImageURL      string `json:"banner_image_url"`
	BannerImageTemplate string `json:"banner_image_template"`
	AvatarImageURL      string `json:"avatar_image_url"`
	AvatarImageTemplate string `json:"avatar_image_template"`
}

type InternalGetCommunitySettingsResponse struct {
	CommunityID         string `json:"community_id"`
	Name                string `json:"name"`
	DisplayName         string `json:"display_name"`
	Type                string `json:"type"`
	OwnerUserID         string `json:"owner_user_id"`
	ShortDescription    string `json:"short_description"`
	LongDescription     string `json:"long_description"`
	LongDescriptionHTML string `json:"long_description_html"`
	Rules               string `json:"rules"`
	RulesHTML           string `json:"rules_html"`
	Email               string `json:"email"`
	Language            string `json:"language"`
	BannerImageURL      string `json:"banner_image_url"`
	BannerImageTemplate string `json:"banner_image_template"`
	AvatarImageURL      string `json:"avatar_image_url"`
	AvatarImageTemplate string `json:"avatar_image_template"`
	TOSBanned           bool   `json:"tos_banned"`
}

// BulkGetCommunitySettingsRequest is the body for the POST /v1/communities/settings/bulk_get endpoint
type BulkGetCommunitySettingsRequest struct {
	CommunityIDs []string `json:"community_ids"`
}

// BulkGetCommunitySettingsByNameRequest is the body for the POST /v1/communities/settings/bulk_get_by_name endpoint
type BulkGetCommunitySettingsByNameRequest struct {
	CommunityNames []string `json:"community_names"`
}

// BulkGetCommunitySettingsResponse is the response for the POST /v1/communities/settings/bulk_get endpoint
// It returns a map from community ID to settings response
type BulkGetCommunitySettingsResponse struct {
	Communities map[string]Community `json:"communities"`
}

// SetCommunitySettingsRequest is the body for the POST /v1/communities/settings/set endpoint
type SetCommunitySettingsRequest struct {
	CommunityID      string  `json:"community_id"`
	DisplayName      *string `json:"display_name"`
	ShortDescription *string `json:"short_description"`
	LongDescription  *string `json:"long_description"`
	Rules            *string `json:"rules"`
	Email            *string `json:"email"`
	Language         *string `json:"language"`
	OwnerUserID      *string `json:"owner_user_id"`
	Type             *string `json:"type"`
}

// CreateCommunityRequest is the body for the POST /v1/communities/create endpoint
type CreateCommunityRequest struct {
	UserID           string  `json:"user_id"`
	DisplayName      string  `json:"display_name"`
	Type             string  `json:"type"`
	Name             string  `json:"name"`
	ShortDescription string  `json:"short_description"`
	LongDescription  string  `json:"long_description"`
	Rules            string  `json:"rules"`
	Language         *string `json:"language"`
}

// CreateCommunityResponse is the response from the POST /v1/communities/create endpoint
type CreateCommunityResponse struct {
	CommunityID string `json:"community_id"`
}

// UploadCommunityImageRequest is the body for the POST /v1/communities/images/upload endpoint
type UploadCommunityImageRequest struct {
	CommunityID      string `json:"community_id"`
	Type             string `json:"type"`
	ImageBytesBase64 string `json:"image_bytes_base_64"`
}

// ImageTypeAvatar is the type field for an avatar image
const ImageTypeAvatar = "avatar"

// ImageTypeBanner is the type field for a banner image
const ImageTypeBanner = "banner"

// RemoveCommunityImageRequest is the body for the POST /v1/communities/images/remove endpoint
type RemoveCommunityImageRequest struct {
	CommunityID string `json:"community_id"`
	Type        string `json:"type"`
}

// GetCommunityModRequest is the body for the POST /v1/communities/mods/get endpoint
type GetCommunityModRequest struct {
	CommunityID string `json:"community_id"`
	UserID      string `json:"user_id"`
}

// GetCommunityModResponse is the response for the POST /v1/communities/mods/get endpoint
type GetCommunityModResponse struct {
	IsMod bool `json:"is_mod"`
}

// ListCommunityModsRequest is the body for the POST /v1/communities/mods/list endpoint
type ListCommunityModsRequest struct {
	CommunityID string `json:"community_id"`
}

// ListCommunityModsResponse is the response for the POST /v1/communities/mods/list endpoint
type ListCommunityModsResponse struct {
	Mods []string `json:"mods"`
}

// AddCommunityModRequest is the body for the POST /v1/communities/mods/add endpoint
type AddCommunityModRequest struct {
	CommunityID  string `json:"community_id"`
	TargetUserID string `json:"target_user_id"`
}

// RemoveCommunityModRequest is the body for the POST /v1/communities/mods/remove endpoint
type RemoveCommunityModRequest struct {
	CommunityID  string `json:"community_id"`
	TargetUserID string `json:"target_user_id"`
}

// GetCommunityBanRequest is the body for the POST /v1/communities/bans/get endpoint
type GetCommunityBanRequest struct {
	CommunityID string `json:"community_id"`
	UserID      string `json:"user_id"`
}

// GetCommunityBanResponse is the response for the POST /v1/communities/bans/get endpoint
type GetCommunityBanResponse struct {
	IsBanned  bool      `json:"is_banned"`
	Start     time.Time `json:"start"`
	ModUserID string    `json:"mod_user_id"`
	Reason    string    `json:"reason"`
}

// BulkGetCommunityBansRequest is the body for the POST /v1/communities/bans/bulk_get endpoint
type BulkGetCommunityBansRequest struct {
	Keys []CommunityBanKey `json:"keys"`
}

// CommunityBanKey is the inner struct for the POST /v1/communities/bans/bulk_get endpoint
type CommunityBanKey struct {
	CommunityID string `json:"community_id"`
	UserID      string `json:"user_id"`
}

// BulkGetCommunityBansResponse is the response for the POST /v1/communities/bans/bulk_get endpoint
type BulkGetCommunityBansResponse struct {
	// If a requested ban is not returned in this list, it does not exist
	Bans []CommunityBan `json:"bans"`
}

// ListCommunityBansRequest is the body for the POST /v1/communities/bans/list endpoint
type ListCommunityBansRequest struct {
	CommunityID string `json:"community_id"`
	Cursor      string `json:"cursor"`
	Limit       int    `json:"limit"`
}

// ListCommunityBansResponse is the response for the POST /v1/communities/bans/list endpoint
// Use the cursor to request the next set of bans
type ListCommunityBansResponse struct {
	Cursor string         `json:"cursor"`
	Bans   []CommunityBan `json:"bans"`
}

// CommunityBan is the inner struct for the ListCommunityBansResponse
type CommunityBan struct {
	CommunityID string    `json:"community_id"`
	UserID      string    `json:"user_id"`
	Start       time.Time `json:"start"`
	ModUserID   string    `json:"mod_user_id"`
	Reason      string    `json:"reason"`
}

// AddCommunityBanRequest is the body for the POST /v1/communities/bans/add endpoint
type AddCommunityBanRequest struct {
	CommunityID  string `json:"community_id"`
	TargetUserID string `json:"target_user_id"`
	Reason       string `json:"reason"`
}

// RemoveCommunityBanRequest is the body for the POST /v1/communities/bans/remove endpoint
type RemoveCommunityBanRequest struct {
	CommunityID  string `json:"community_id"`
	TargetUserID string `json:"target_user_id"`
}

// CommunityModerationAction is a moderation action that was taken in a community
type CommunityModerationAction struct {
	CommunityID  string     `json:"community_id"`
	ModUserID    string     `json:"mod_user_id"`
	TargetUserID string     `json:"target_user_id"`
	Type         string     `json:"action_type"`
	Reason       string     `json:"reason"`
	StartTime    time.Time  `json:"start_time"`
	EndTime      *time.Time `json:"end_time,omitempty"`
}

// GetCommunityModerationActionsRequest is the body for the POST /v1/communities/mod_actions endpoint
type GetCommunityModerationActionsRequest struct {
	Cursor      string `json:"cursor"`
	Limit       int    `json:"limit"`
	CommunityID string `json:"community_id"`
	Filter      string `json:"filter,omitempty"`
}

// GetCommunityModerationActionsResponse is the response for the POST /v1/communities/mod_actions endpoint
type GetCommunityModerationActionsResponse struct {
	Cursor  string                      `json:"cursor"`
	Total   int64                       `json:"total"`
	Actions []CommunityModerationAction `json:"moderation_actions"`
}

// GetCommunityTimeoutRequest is the body for the POST /v1/communities/timeouts/get endpoint
type GetCommunityTimeoutRequest struct {
	CommunityID string `json:"community_id"`
	UserID      string `json:"user_id"`
}

// GetCommunityTimeoutResponse is the response for the POST /v1/communities/Timeouts/get endpoint
type GetCommunityTimeoutResponse struct {
	IsTimedOut bool      `json:"is_timed_out"`
	Start      time.Time `json:"start"`
	Expiration time.Time `json:"expiration"`
	ModUserID  string    `json:"mod_user_id"`
	Reason     string    `json:"reason"`
}

// BulkGetCommunityTimeoutsRequest is the body for the POST /v1/communities/timeouts/bulk_get endpoint
type BulkGetCommunityTimeoutsRequest struct {
	Keys []CommunityTimeoutKey `json:"keys"`
}

// CommunityTimeoutKey is the inner struct for the POST /v1/communities/timeouts/bulk_get endpoint
type CommunityTimeoutKey struct {
	CommunityID string `json:"community_id"`
	UserID      string `json:"user_id"`
}

// BulkGetCommunityTimeoutsResponse is the response for the POST /v1/communities/timeouts/bulk_get endpoint
type BulkGetCommunityTimeoutsResponse struct {
	// If a requested timeout is not returned in this list, it does not exist
	Timeouts []CommunityTimeout `json:"timeouts"`
}

// ListCommunityTimeoutsRequest is the body for the POST /v1/communities/timeouts/list endpoint
type ListCommunityTimeoutsRequest struct {
	CommunityID string `json:"community_id"`
	Cursor      string `json:"cursor"`
	Limit       int    `json:"limit"`
}

// ListCommunityTimeoutsResponse is the response for the POST /v1/communities/Timeouts/list endpoint
// Use the cursor to request the next set of Timeouts
type ListCommunityTimeoutsResponse struct {
	Cursor   string             `json:"cursor"`
	Timeouts []CommunityTimeout `json:"timeouts"`
}

// CommunityTimeout is the inner struct for the ListCommunityTimeoutsResponse
type CommunityTimeout struct {
	CommunityID string    `json:"community_id"`
	UserID      string    `json:"user_id"`
	Start       time.Time `json:"start"`
	Expiration  time.Time `json:"expiration"`
	ModUserID   string    `json:"mod_user_id"`
	Reason      string    `json:"reason"`
}

// AddCommunityTimeoutRequest is the body for the POST /v1/communities/timeouts/add endpoint
type AddCommunityTimeoutRequest struct {
	CommunityID  string `json:"community_id"`
	TargetUserID string `json:"target_user_id"`
	Duration     int64  `json:"duration_seconds"`
	Reason       string `json:"reason"`
}

// RemoveCommunityTimeoutRequest is the body for the POST /v1/communities/timeouts/remove endpoint
type RemoveCommunityTimeoutRequest struct {
	CommunityID  string `json:"community_id"`
	TargetUserID string `json:"target_user_id"`
}

// GetCommunityPermissionsRequest is the body for the POST /v1/communities/permissions endpoint
type GetCommunityPermissionsRequest struct {
	CommunityID string `json:"community_id"`
	UserID      string `json:"user_id"`
}

// GetCommunityPermissionsResponse is the response for the POST /v1/communities/permissions endpoint
type GetCommunityPermissionsResponse struct {
	Ban       bool `json:"ban"`
	Timeout   bool `json:"timeout"`
	Edit      bool `json:"edit"`
	Broadcast bool `json:"broadcast"`
}

// SetChannelCommunityRequest is the body for the POST /v1/channels/communities/set endpoint
type SetChannelCommunityRequest struct {
	ChannelID   string `json:"channel_id"`
	CommunityID string `json:"community_id"`
}

// SetChannelCommunitiesRequest is the body for the POST /v1/channels/communities/set_multiple endpoint
type SetChannelCommunitiesRequest struct {
	ChannelID    string   `json:"channel_id"`
	CommunityIDs []string `json:"community_ids"`
}

// UnsetChannelCommunityRequest is the body for the POST /v1/channels/communities/unset endpoint
type UnsetChannelCommunityRequest struct {
	ChannelID string `json:"channel_id"`
}

// GetChannelCommunityRequest is the body for the POST /v1/channels/communities/get endpoint
type GetChannelCommunityRequest struct {
	ChannelID string `json:"channel_id"`
}

// GetChannelCommunityResponse is the response for the POST /v1/channels/communities/get endpoint
type GetChannelCommunityResponse struct {
	CommunityID  string   `json:"community_id"`
	CommunityIDs []string `json:"community_ids"`
}

// GetRecentChannelCommunitiesRequest is the body for the POST /v1/channels/communities/recent endpoint
type GetRecentChannelCommunitiesRequest struct {
	ChannelID string `json:"channel_id"`
}

// GetRecentChannelCommunitiesResponse is the response for the POST /v1/channels/communities/recent endpoint
type GetRecentChannelCommunitiesResponse struct {
	Communities []string `json:"communities"`
}

// BulkGetChannelCommunitiesByIDsRequest is the body for the POST /v1/channels/communities/bulk_get_by_ids endpoint
type BulkGetChannelCommunitiesByIDsRequest struct {
	ChannelIDs []string `json:"channel_ids"`
}

// BulkGetChannelCommunitiesByIDsResponse is the response for the POST /v1/channels/communities/bulk_get_by_ids endpoint
type BulkGetChannelCommunitiesByIDsResponse struct {
	// A map of channel ID to community IDs
	CommunityIDs map[string][]string `json:"community_ids"`
}

// BulkGetChannelCommunitiesRequest is the body for the POST /v1/channels/communities/bulk endpoint
type BulkGetChannelCommunitiesRequest struct {
	ChannelLogins []string `json:"channel_logins"`
}

// BulkGetChannelCommunitiesResponse is the response for the POST /v1/channels/communities/bulk endpoint
type BulkGetChannelCommunitiesResponse struct {
	Results []BulkGetChannelCommunitiesResult `json:"results"`
}

// BulkGetChannelCommunitiesResult is the inner JSON for the bulk get endpoint
type BulkGetChannelCommunitiesResult struct {
	ChannelID    string   `json:"channel_id"`
	ChannelLogin string   `json:"channel_login"`
	CommunityID  string   `json:"community_id"`
	CommunityIDs []string `json:"community_ids"`
}

// ReportChannelCommunityRequest is the body for the POST /v1/channels/communities/report endpoint
type ReportChannelCommunityRequest struct {
	UserID      string `json:"user_id"`
	ChannelID   string `json:"channel_id"`
	CommunityID string `json:"community_id"`
	Description string `json:"description"`
}

// ReportCommunityRequest is the body for the POST /v1/communities/report endpoint
type ReportCommunityRequest struct {
	UserID      string `json:"user_id"`
	CommunityID string `json:"community_id"`
	Reason      string `json:"reason"`
	Description string `json:"description"`
}

// Valid reasons for a community report
const (
	ReasonBitsViolation         = "bits_violation"
	ReasonCheating              = "cheating"
	ReasonGore                  = "gore"
	ReasonHarassment            = "harassment"
	ReasonHarm                  = "harm"
	ReasonHateSpeech            = "hate_speech"
	ReasonImpersonation         = "impersonation"
	ReasonMusicConduct          = "music_conduct"
	ReasonNongaming             = "nongaming"
	ReasonOffensiveUsername     = "offensive_username"
	ReasonOther                 = "other"
	ReasonPorn                  = "porn"
	ReasonProhibited            = "prohibited"
	ReasonSelfharm              = "selfharm"
	ReasonSocialEatingViolation = "social_eating_violation"
	ReasonSpam                  = "spam"
	ReasonTosBanEvasion         = "tos_ban_evasion"
	ReasonUnderaged             = "underaged"
	ReasonCommunityTOSViolation = "community_tos_violation"
)

// TOSBanCommunityRequest is the body for the POST /v1/communities/tos_ban endpoint
type TOSBanCommunityRequest struct {
	CommunityID string `json:"community_id"`
}

// DeleteRequest is the body for the POST /v1/communities/admin_delete endpoint
type DeleteCommunityRequest struct {
	CommunityID string `json:"community_id"`
}

// ListCommunityReservedNamesRequest is the request for the POST /v1/communities/reserved_names/list endpoint
// We're intentionally using per_page/page here rather than a cursor
// because this is an internal endpoint used by a rails app (admin-panel)
// and paging is easier this way with rails.
type ListCommunityReservedNamesRequest struct {
	Page    int    `json:"page"`
	PerPage int    `json:"per_page"`
	Filter  string `json:"filter"`
}

// ListCommunityReservedNamesResponse is the response for the POST /v1/communities/reserved_names/list endpoint
type ListCommunityReservedNamesResponse struct {
	TotalPages    int                     `json:"total_pages"`
	ReservedNames []CommunityReservedName `json:"reserved_names"`
}

// CommunityReservedName is the internal structure for ListCommunityReservedNamesResponse
type CommunityReservedName struct {
	Name string `json:"reserved_name"`
}

// CreateCommunityReservedNameRequest is the response for the POST /v1/communities/reserved_names/create endpoint
type CreateCommunityReservedNameRequest struct {
	ReservedName string `json:"reserved_name"`
}

// DeleteCommunityReservedNameRequest is the response for the POST /v1/communities/reserved_names/delete endpoint
type DeleteCommunityReservedNameRequest struct {
	ReservedName string `json:"reserved_name"`
}

// CommunityFollower is the inner struct for community followers api responses
type CommunityFollower struct {
	CommunityID string    `json:"community_id"`
	UserID      string    `json:"user_id"`
	CreatedAt   time.Time `json:"created_at"`
}

// ListCommunityFollowersRequest is the request for the POST /v1/communities/followers/list endpoint
type ListCommunityFollowersRequest struct {
	CommunityID string `json:"community_id"`
	Limit       int    `json:"limit"`
	Cursor      string `json:"cursor"`
}

// ListCommunityFollowersResponse is the response for the POST /v1/communities/followers/list endpoint
type ListCommunityFollowersResponse struct {
	Total     int                 `json:"total"`
	Cursor    string              `json:"cursor"`
	Followers []CommunityFollower `json:"followers"`
}

// CountCommunityFollowersRequest is the request for the POST /v1/communities/followers/list endpoint
type CountCommunityFollowersRequest struct {
	CommunityID string `json:"community_id"`
}

// CountCommunityFollowersResponse is the response for the POST /v1/communities/followers/list endpoint
type CountCommunityFollowersResponse struct {
	Followers int `json:"followers"`
}

// GetCommunityFollowerRequest is the request for the POST /v1/communities/followers/get endpoint
type GetCommunityFollowerRequest struct {
	CommunityID string `json:"community_id"`
	UserID      string `json:"user_id"`
}

// GetCommunityFollowerResponse is the response for the POST /v1/communities/followers/get endpoint
type GetCommunityFollowerResponse struct {
	Follower *CommunityFollower `json:"follower"`
}

// FollowedCommunity is the inner struct for followed community api responses
type FollowedCommunity struct {
	CommunityID    string    `json:"community_id"`
	Name           string    `json:"name"`
	Type           string    `json:"type"`
	DisplayName    string    `json:"display_name"`
	AvatarImageURL string    `json:"avatar_image_url"`
	CreatedAt      time.Time `json:"created_at"`
}

// ListUserFollowedCommunitiesRequest is the request for the POST /v1/users/follows/communities/list endpoint
type ListUserFollowedCommunitiesRequest struct {
	UserID string `json:"user_id"`
	Limit  int    `json:"limit"`
	Cursor string `json:"cursor"`
}

// ListUserFollowedCommunitiesResponse is the response for the POST /v1/users/follows/communities/list endpoint
type ListUserFollowedCommunitiesResponse struct {
	Total               int                 `json:"total"`
	Cursor              string              `json:"cursor"`
	FollowedCommunities []FollowedCommunity `json:"followed_communities"`
}

// TopUserFollowedCommunitiesRequest is the request for the POST /v1/users/follows/communities/top endpoint
type TopUserFollowedCommunitiesRequest struct {
	UserID string `json:"user_id"`
	Limit  int    `json:"limit"`
	Cursor string `json:"cursor"`
}

// TopUserFollowedCommunitiesResponse is the response from the POST /v1/users/follows/communities/top endpoint
type TopUserFollowedCommunitiesResponse struct {
	Total   int                  `json:"total"`
	Cursor  string               `json:"cursor"`
	Results []TopCommunityResult `json:"results"`
}

// AddUserFollowedCommunityRequest is the request for the POST /v1/users/follows/communities/add endpoint
type AddUserFollowedCommunityRequest struct {
	UserID      string `json:"user_id"`
	CommunityID string `json:"community_id"`
}

// RemoveUserFollowedCommunityRequest is the request for the POST /v1/users/follows/communities/remove endpoint
type RemoveUserFollowedCommunityRequest struct {
	UserID      string `json:"user_id"`
	CommunityID string `json:"community_id"`
}

// CreateGameCommunityRequest is the request for the POST /v1/games/communities/create endpoint
type CreateGameCommunityRequest struct {
	GameID string `json:"game_id"`
}

// ListUserBlocksParams is the body for the POST /v1/users/blocks/get endpoint
type ListUserBlocksParams struct {
	UserID string `json:"user_id"`
}

// ListUserBlocksResponse is the response for POST /v1/users/blocks/get endpoint
type ListUserBlocksResponse struct {
	BlockedUserIDs []string `json:"blocked_user_ids"`
}

// AddUserBlockParams is the body for the POST /v1/users/blocks/add endpoint
type AddUserBlockParams struct {
	UserID       string `json:"user_id"`
	TargetUserID string `json:"target_user_id"`
	Reason       string `json:"reason"`
	// SourceContext identifies what user flow initiated the block (e.g. chat, whisper).
	SourceContext string `json:"source_context"`
}

// AddUserBlockResponse is the response for the POST /v1/users/blocks/add endpoint
type AddUserBlockResponse struct {
	Status  int    `json:"status"`
	Error   string `json:"error"`
	Message string `json:"message"`
}

// RemoveUserBlockParams is the body for the POST /v1/users/blocks/remove endpoint
type RemoveUserBlockParams struct {
	UserID       string `json:"user_id"`
	TargetUserID string `json:"target_user_id"`
}

// ParseMessageRequest is the body for the POST /v1/messages/parse endpoint
type ParseMessageRequest struct {
	ChannelID string `json:"channel_id"`
	UserID    string `json:"user_id"`
	Body      string `json:"body"`
}

// ParseMessageResponse is the response of the POST /v1/messages/parse endpoint
type ParseMessageResponse struct {
	Status      ParseMessageStatus  `json:"status"`
	Message     *ParseMessageResult `json:"message,omitempty"`
	BannedUntil *time.Time          `json:"banned_until,omitempty"`
}

// ParseMessageResult is the inner JSON for the parse message endpoint
type ParseMessageResult struct {
	Body            string                      `json:"body"`
	Emoticons       []ParseMessageEmoticonRange `json:"emoticons"`
	UserBadges      []ParseMessageUserBadge     `json:"user_badges"`
	UserColor       string                      `json:"user_color"`
	UserLogin       string                      `json:"user_login"`
	UserDisplayName string                      `json:"user_display_name"`
}

// ParseMessageEmoticonRange defines the JSON structure for an emoticon range
type ParseMessageEmoticonRange struct {
	ParseMessageBodyRange
	ID string `json:"id"`
}

// ParseMessageBodyRange defines the JSON structure for an arbitrary range
type ParseMessageBodyRange struct {
	Begin int `json:"begin"`
	End   int `json:"end"`
}

type ParseMessageUserBadge struct {
	ID      string `json:"id"`
	Version string `json:"version"`
}

type ParseMessageStatus string

const (
	ParseMessageStatusUserBanned      = "user_banned"
	ParseMessageStatusUserNotVerified = "user_not_verified"
	ParseMessageStatusReviewRequired  = "review_required"
	ParseMessageStatusOK              = "ok"
)

// CheckEmoticonsAccessRequest is the body for the POST /v1/users/emoticons/check_access endpoint
type CheckEmoticonsAccessRequest struct {
	UserID      string   `json:"user_id"`
	EmoticonIDs []string `json:"emoticon_ids"`
}

// CheckEmoticonsAccessResponse is the response for the POST /v1/users/emoticons/check_access endpoint
type CheckEmoticonsAccessResponse struct {
	UserEmoticonSetIDs []string `json:"user_emoticon_set_ids"`
	NoAccessIDs        []string `json:"no_access_ids"`
}

// GetEmoticonIDsRequest is the body for the POST /v1/users/emoticons/get_ids endpoint
type GetEmoticonIDsRequest struct{}

// GetEmoticonIDsResponse is the response for the POST /v1/users/emoticons/get_ids endpoint
type GetEmoticonIDsResponse struct {
	EmoticonIDs []string `json:"emoticon_ids"`
}

type BrowseSortType string

const (
	BrowseSortTypeViewers   BrowseSortType = "viewers"
	BrowseSortTypeRelevancy                = "relevancy"
)

// GetTopBrowseParams is the body for the GET /v1/browse/top endpoint
type GetTopBrowseParams struct {
	SortBy BrowseSortType `json:"sort_by"`
	Filter string         `json:"filter"`
	Cursor string         `json:"cursor"`
	Limit  int            `json:"limit"`

	parsedCursor *int
}

func (p *GetTopBrowseParams) Validate() error {
	if p.SortBy != "" &&
		p.SortBy != BrowseSortTypeViewers &&
		p.SortBy != BrowseSortTypeRelevancy {
		return errors.New("invalid sort_by value")
	}

	if p.Filter != "" && p.Filter != "all" {
		for _, filter := range strings.Split(p.Filter, BrowseTopFilterSeparator) {
			switch filter {
			case BrowseTopFilterGames, BrowseTopFilterCommunities, BrowseTopFilterCreative:
				continue
			default:
				return errors.New("invalid filter value (expected 'games', 'communities' or 'creative', separated by ':')")
			}
		}
	}

	if p.Limit < 0 || p.Limit > maxBrowseLimit {
		return errors.New(fmt.Sprintf("limit must be between 0 and %d", maxBrowseLimit))
	}

	offset, err := p.GetCursor()
	if err != nil {
		return err
	}
	if offset < 0 || offset > maxBrowseOffset {
		return errors.New(fmt.Sprintf("invalid cursor"))
	}

	return nil
}

func (p *GetTopBrowseParams) GetCursor() (int, error) {
	if p.parsedCursor != nil {
		return *p.parsedCursor, nil
	}
	offset, err := CursorToInt(p.Cursor)
	if err != nil {
		return 0, err
	}
	p.parsedCursor = &offset
	return *p.parsedCursor, err
}

// GetTopBrowseResponse is the response for the GET /v1/browse/top endpoint
type GetTopBrowseResponse struct {
	Total  int           `json:"_total"`
	Cursor string        `json:"_cursor"`
	Top    []BrowseEntry `json:"top"`
}

// BrowseEntry is the inner struct for GetTopBrowseResponse, it's a unified
// type with all the info that the frontend will need to display this category
// on a browse page. Used for games and communities
type BrowseEntry struct {
	ID       string `json:"_id"`
	Type     string `json:"type"`
	Channels int    `json:"channels"`
	Viewers  int    `json:"viewers"`
}

// GetGameBannerImagesRequest is the body for the POST /v1/games/banner_images/bulk_get endpoint
type GetGameBannerImagesRequest struct {
	GameIDs []string `json:"game_ids"`
}

// GetGameBannerImagesResponse is the response from the POST /v1/games/banner_images/bulk_get endpoint
type GetGameBannerImagesResponse struct {
	// Banners is a map from game ID to banner info
	Banners map[string]*GameBanner `json:"banners"`
}

// GameBanner is the inner struct for GetGameBannerImagesResponse
type GameBanner struct {
	GameID              string `json:"game_id"`
	BannerImageTemplate string `json:"banner_image_template"`
}

// UploadGameBannerImageRequest is the body for the POST /v1/games/banner_images/upload endpoint
type UploadGameBannerImageRequest struct {
	GameID           string `json:"game_id"`
	ImageBytesBase64 string `json:"image_bytes_base_64"`
}
