package jikan

import (
	"fmt"
	"strconv"
	"strings"
)

type SearchResult struct {
	Animes      []Anime
	HasNextPage bool
}

type TopAnimeResult struct {
	Animes      []Anime
	HasNextPage bool
}

type StudioAnimeResult struct {
	Animes      []Anime
	HasNextPage bool
	StudioName  string
}

type NamedEntity struct {
	MalID int    `json:"mal_id"`
	Name  string `json:"name"`
}

type Aired struct {
	From   string `json:"from"`
	To     string `json:"to"`
	String string `json:"string"`
}

type TitleEntry struct {
	Type  string `json:"type"`
	Title string `json:"title"`
}

type Anime struct {
	MalID         int          `json:"mal_id"`
	Title         string       `json:"title"`
	TitleEnglish  string       `json:"title_english"`
	TitleJapanese string       `json:"title_japanese"`
	TitleSynonyms []string     `json:"title_synonyms"`
	Titles        []TitleEntry `json:"titles"`
	Images        struct {
		Jpg struct {
			LargeImageURL string `json:"large_image_url"`
		} `json:"jpg"`
		Webp struct {
			LargeImageURL string `json:"large_image_url"`
		} `json:"webp"`
	} `json:"images"`
	Synopsis     string        `json:"synopsis"`
	Rank         int           `json:"rank"`
	Popularity   int           `json:"popularity"`
	Status       string        `json:"status"`
	Airing       bool          `json:"airing"`
	Episodes     int           `json:"episodes"`
	Score        float64       `json:"score"`
	Season       string        `json:"season"`
	Year         int           `json:"year"`
	Type         string        `json:"type"`
	Rating       string        `json:"rating"`
	Duration     string        `json:"duration"`
	ScoredBy     int           `json:"scored_by"`
	Aired        Aired         `json:"aired"`
	Genres       []NamedEntity `json:"genres"`
	Studios      []NamedEntity `json:"studios"`
	Producers    []NamedEntity `json:"producers"`
	Themes       []NamedEntity `json:"themes"`
	Source       string        `json:"source"`
	Background   string        `json:"background"`
	Favorites    int           `json:"favorites"`
	Members      int           `json:"members"`
	Demographics []NamedEntity `json:"demographics"`
	Licensors    []NamedEntity `json:"licensors"`
	Broadcast    struct {
		Day      string `json:"day"`
		Time     string `json:"time"`
		Timezone string `json:"timezone"`
		String   string `json:"string"`
	} `json:"broadcast"`
	Trailer struct {
		YoutubeID string `json:"youtube_id"`
		URL       string `json:"url"`
		EmbedURL  string `json:"embed_url"`
		Images    struct {
			ImageURL        string `json:"image_url"`
			SmallImageURL   string `json:"small_image_url"`
			MediumImageURL  string `json:"medium_image_url"`
			LargeImageURL   string `json:"large_image_url"`
			MaximumImageURL string `json:"maximum_image_url"`
		} `json:"images"`
	} `json:"trailer"`
	Streaming []struct {
		Name string `json:"name"`
		URL  string `json:"url"`
	} `json:"streaming"`
	Relations []JikanRelationGroup `json:"relations"`
	External  []struct {
		Name string `json:"name"`
		URL  string `json:"url"`
	} `json:"external"`
}

type CharacterVoiceActor struct {
	Person struct {
		MalID  int    `json:"mal_id"`
		URL    string `json:"url"`
		Images struct {
			Jpg struct {
				ImageURL string `json:"image_url"`
			} `json:"jpg"`
		} `json:"images"`
		Name string `json:"name"`
	} `json:"person"`
	Language string `json:"language"`
}

type CharacterEntry struct {
	Character struct {
		MalID  int    `json:"mal_id"`
		URL    string `json:"url"`
		Images struct {
			Jpg struct {
				ImageURL string `json:"image_url"`
			} `json:"jpg"`
			Webp struct {
				ImageURL      string `json:"image_url"`
				SmallImageURL string `json:"small_image_url"`
			} `json:"webp"`
		} `json:"images"`
		Name string `json:"name"`
	} `json:"character"`
	Role        string                `json:"role"`
	VoiceActors []CharacterVoiceActor `json:"voice_actors"`
}

type CharactersResponse struct {
	Data []CharacterEntry `json:"data"`
}

type RecommendationEntry struct {
	Entry struct {
		MalID  int    `json:"mal_id"`
		URL    string `json:"url"`
		Images struct {
			Webp struct {
				LargeImageURL string `json:"large_image_url"`
			} `json:"webp"`
		} `json:"images"`
		Title string `json:"title"`
	} `json:"entry"`
	URL   string `json:"url"`
	Votes int    `json:"votes"`
}

type RecommendationsResponse struct {
	Data []RecommendationEntry `json:"data"`
}

// ScoredByFormatted returns formatted count (e.g. "1 234 567").
func (a Anime) ScoredByFormatted() string {
	return formatNumber(a.ScoredBy)
}

// MembersFormatted returns formatted count (e.g. "1 234 567").
func (a Anime) MembersFormatted() string {
	return formatNumber(a.Members)
}

// FavoritesFormatted returns formatted count (e.g. "1 234 567").
func (a Anime) FavoritesFormatted() string {
	return formatNumber(a.Favorites)
}

// formatNumber adds space separators to a number (1234567 -> "1 234 567").
func formatNumber(n int) string {
	if n == 0 {
		return ""
	}
	s := fmt.Sprintf("%d", n)
	var res []string
	for i := len(s); i > 0; i -= 3 {
		start := max(i-3, 0)
		res = append([]string{s[start:i]}, res...)
	}
	return strings.Join(res, " ")
}

// ImageURL returns the webp large image URL for the anime.
func (a Anime) ImageURL() string {
	return a.Images.Webp.LargeImageURL
}

// ShortRating extracts just the rating code (e.g. "PG-13") from full rating string.
func (a Anime) ShortRating() string {
	if a.Rating == "" {
		return ""
	}
	// Rating format: "PG-13 - Teens 13 or older"
	for i, c := range a.Rating {
		if c == ' ' && i > 0 {
			return a.Rating[:i]
		}
	}
	return a.Rating
}

// ShortDuration extracts numeric duration in minutes (e.g. "23m" from "23 min per ep").
func (a Anime) ShortDuration() string {
	if a.Duration == "" {
		return ""
	}
	// Duration format: "23 min per ep" or "1 hr 30 min"
	var num strings.Builder
	for _, c := range a.Duration {
		if c >= '0' && c <= '9' {
			num.WriteString(string(c))
		} else if c == ' ' && num.String() != "" {
			break
		}
	}
	if num.String() != "" {
		return num.String() + "m"
	}
	return a.Duration
}

// DurationSeconds converts duration string to total seconds (e.g. "1 hr 30 min" -> 5400).
func (a Anime) DurationSeconds() float64 {
	if a.Duration == "" {
		return 0
	}
	var hours, minutes int
	var currentValue int
	hasValue := false

	for token := range strings.FieldsSeq(strings.ToLower(a.Duration)) {
		value, err := strconv.Atoi(token)
		if err == nil {
			currentValue = value
			hasValue = true
			continue
		}
		if !hasValue {
			continue
		}

		switch {
		case strings.HasPrefix(token, "h"):
			hours = currentValue
			hasValue = false
		case strings.HasPrefix(token, "m"):
			minutes = currentValue
			hasValue = false
		}
	}

	if hasValue {
		minutes = currentValue
	}

	return float64(hours*60+minutes) * 60
}

// Premiered returns formatted premiere string (e.g. "Winter 2020").
func (a Anime) Premiered() string {
	if a.Season != "" && a.Year > 0 {
		return fmt.Sprintf("%s %d", seasonLabel(a.Season), a.Year)
	}
	return ""
}

// seasonLabel normalizes season string to title case (fall/autumn -> Fall).
func seasonLabel(season string) string {
	switch strings.ToLower(season) {
	case "winter":
		return "Winter"
	case "spring":
		return "Spring"
	case "summer":
		return "Summer"
	case "fall", "autumn":
		return "Fall"
	default:
		if season == "" {
			return ""
		}
		return strings.ToUpper(season[:1]) + strings.ToLower(season[1:])
	}
}

type AnimeResponse struct {
	Data Anime `json:"data"`
}

type Genre struct {
	MalID int    `json:"mal_id"`
	Name  string `json:"name"`
}

type GenresResponse struct {
	Data []Genre `json:"data"`
}

type SearchResponse struct {
	Data       []Anime    `json:"data"`
	Pagination Pagination `json:"pagination"`
}

type Pagination struct {
	LastVisiblePage int  `json:"last_visible_page"`
	HasNextPage     bool `json:"has_next_page"`
}

type TopAnimeResponse struct {
	Data       []Anime    `json:"data"`
	Pagination Pagination `json:"pagination"`
}

type EpisodeImages struct {
	Jpg struct {
		ImageURL string `json:"image_url"`
	} `json:"jpg"`
}

type Episode struct {
	MalID   int            `json:"mal_id"`
	Title   string         `json:"title"`
	Episode string         `json:"episode"`
	Filler  bool           `json:"filler"`
	Recap   bool           `json:"recap"`
	Images  *EpisodeImages `json:"images,omitempty"`
}

type EpisodesResponse struct {
	Data       []Episode  `json:"data"`
	Pagination Pagination `json:"pagination"`
}

type EpisodeResponse struct {
	Data Episode `json:"data"`
}

type JikanRelationEntry struct {
	MalID int    `json:"mal_id"`
	Type  string `json:"type"`
	Name  string `json:"name"`
	URL   string `json:"url"`
}

type JikanRelationGroup struct {
	Relation string               `json:"relation"`
	Entry    []JikanRelationEntry `json:"entry"`
}

type JikanRelationsResponse struct {
	Data []JikanRelationGroup `json:"data"`
}

type RelationEntry struct {
	Anime     Anime
	Relation  string
	IsCurrent bool
	IsExtra   bool
}

// Staff types

type StaffEntry struct {
	Person struct {
		MalID  int    `json:"mal_id"`
		URL    string `json:"url"`
		Images struct {
			Jpg struct {
				ImageURL string `json:"image_url"`
			} `json:"jpg"`
		} `json:"images"`
		Name string `json:"name"`
	} `json:"person"`
	Positions []string `json:"positions"`
}

type StaffResponse struct {
	Data []StaffEntry `json:"data"`
}

type StatisticsScore struct {
	Score      int     `json:"score"`
	Votes      int     `json:"votes"`
	Percentage float64 `json:"percentage"`
}

type Statistics struct {
	Watching    int               `json:"watching"`
	Completed   int               `json:"completed"`
	OnHold      int               `json:"on_hold"`
	Dropped     int               `json:"dropped"`
	PlanToWatch int               `json:"plan_to_watch"`
	Total       int               `json:"total"`
	Scores      []StatisticsScore `json:"scores"`
}

type StatisticsResponse struct {
	Data Statistics `json:"data"`
}

type ThemesData struct {
	Openings []string `json:"openings"`
	Endings  []string `json:"endings"`
}

type ThemesResponse struct {
	Data ThemesData `json:"data"`
}

type ReviewReactions struct {
	Overall     int `json:"overall"`
	Nice        int `json:"nice"`
	LoveIt      int `json:"love_it"`
	Funny       int `json:"funny"`
	Confusing   int `json:"confusing"`
	Informative int `json:"informative"`
	WellWritten int `json:"well_written"`
	Creative    int `json:"creative"`
}

type ReviewEntry struct {
	MalID         int             `json:"mal_id"`
	URL           string          `json:"url"`
	Type          string          `json:"type"`
	Reactions     ReviewReactions `json:"reactions"`
	Date          string          `json:"date"`
	Review        string          `json:"review"`
	Score         int             `json:"score"`
	Tags          []string        `json:"tags"`
	IsSpoiler     bool            `json:"is_spoiler"`
	IsPreliminary bool            `json:"is_preliminary"`
	EpisodesSeen  int             `json:"episodes_seen"`
	User          struct {
		URL      string `json:"url"`
		Username string `json:"username"`
		Images   struct {
			Jpg struct {
				ImageURL string `json:"image_url"`
			} `json:"jpg"`
			Webp struct {
				ImageURL string `json:"image_url"`
			} `json:"webp"`
		} `json:"images"`
	} `json:"user"`
}

type ReviewsResponse struct {
	Data       []ReviewEntry `json:"data"`
	Pagination Pagination    `json:"pagination"`
}

// DisplayTitle returns English title if available, otherwise default title, titles[0], then Japanese.
func (a Anime) DisplayTitle() string {
	if a.TitleEnglish != "" {
		return a.TitleEnglish
	}
	if a.Title != "" {
		return a.Title
	}
	if len(a.Titles) > 0 && a.Titles[0].Title != "" {
		return a.Titles[0].Title
	}
	return a.TitleJapanese
}
