package beatleader

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"log"
	"os"
	"strings"
	"time"

	"git.lxtend.com/lixiangwuxian/imagedd/sprite"
	"git.lxtend.com/lixiangwuxian/imagedd/text2img"
	"git.lxtend.com/lixiangwuxian/qqbot/qq_message"
	"git.lxtend.com/lixiangwuxian/qqbot/util"
)

type ScoreData struct {
	ContextExtensions []ContextExtension `json:"contextExtensions"`
	MyScore           *string            `json:"myScore"`
	ValidContexts     int                `json:"validContexts"`
	Leaderboard       Leaderboard        `json:"leaderboard"`
	AccLeft           float64            `json:"accLeft"`
	AccRight          float64            `json:"accRight"`
	ID                int                `json:"id"`
	BaseScore         int                `json:"baseScore"`
	ModifiedScore     int                `json:"modifiedScore"`
	Accuracy          float64            `json:"accuracy"`
	PlayerID          string             `json:"playerId"`
	Pp                float64            `json:"pp"`
	BonusPp           float64            `json:"bonusPp"`
	PassPp            float64            `json:"passPP"`
	AccPp             float64            `json:"accPP"`
	TechPp            float64            `json:"techPP"`
	Rank              int                `json:"rank"`
	Country           string             `json:"country"`
	FcAccuracy        float64            `json:"fcAccuracy"`
	FcPp              float64            `json:"fcPp"`
	Weight            float64            `json:"weight"`
	Replay            string             `json:"replay"`
	Modifiers         string             `json:"modifiers"`
	BadCuts           int                `json:"badCuts"`
	MissedNotes       int                `json:"missedNotes"`
	BombCuts          int                `json:"bombCuts"`
	WallsHit          int                `json:"wallsHit"`
	Pauses            int                `json:"pauses"`
	FullCombo         bool               `json:"fullCombo"`
	Platform          string             `json:"platform"`
	MaxCombo          int                `json:"maxCombo"`
	MaxStreak         *int               `json:"maxStreak"`
	HMD               int                `json:"hmd"`
	Controller        int                `json:"controller"`
	LeaderboardID     string             `json:"leaderboardId"`
	Timeset           string             `json:"timeset"`
	Timepost          int64              `json:"timepost"`
	ReplaysWatched    int                `json:"replaysWatched"`
	PlayCount         int                `json:"playCount"`
	LastTryTime       int64              `json:"lastTryTime"`
	Priority          int                `json:"priority"`
	Player            Player             `json:"player"`
	ScoreImprovement  ScoreImprovement   `json:"scoreImprovement"`
	// RankVoting        *string            `json:"rankVoting"`
	// Metadata          *string            `json:"metadata"`
	Offsets Offsets `json:"offsets"`
}

type ContextExtension struct {
	ID               int              `json:"id"`
	PlayerID         string           `json:"playerId"`
	Weight           float64          `json:"weight"`
	Rank             int              `json:"rank"`
	BaseScore        int              `json:"baseScore"`
	ModifiedScore    int              `json:"modifiedScore"`
	Accuracy         float64          `json:"accuracy"`
	Pp               float64          `json:"pp"`
	PassPp           float64          `json:"passPP"`
	AccPp            float64          `json:"accPP"`
	TechPp           float64          `json:"techPP"`
	BonusPp          float64          `json:"bonusPp"`
	Modifiers        string           `json:"modifiers"`
	Context          int              `json:"context"`
	ScoreImprovement ScoreImprovement `json:"scoreImprovement"`
}

type ScoreImprovement struct {
	ID                    int     `json:"id"`
	Timeset               string  `json:"timeset"`
	Score                 int     `json:"score"`
	Accuracy              float64 `json:"accuracy"`
	Pp                    float64 `json:"pp"`
	BonusPp               float64 `json:"bonusPp"`
	Rank                  int     `json:"rank"`
	AccRight              float64 `json:"accRight"`
	AccLeft               float64 `json:"accLeft"`
	AverageRankedAccuracy float64 `json:"averageRankedAccuracy"`
	TotalPp               float64 `json:"totalPp"`
	TotalRank             int     `json:"totalRank"`
	BadCuts               int     `json:"badCuts"`
	MissedNotes           int     `json:"missedNotes"`
	BombCuts              int     `json:"bombCuts"`
	WallsHit              int     `json:"wallsHit"`
	Pauses                int     `json:"pauses"`
}

type Leaderboard struct {
	ID         string     `json:"id"`
	Song       Song       `json:"song"`
	Difficulty Difficulty `json:"difficulty"`
}

type Song struct {
	ID              string  `json:"id"`
	Hash            string  `json:"hash"`
	Name            string  `json:"name"`
	SubName         string  `json:"subName"`
	Author          string  `json:"author"`
	Mapper          string  `json:"mapper"`
	MapperID        int     `json:"mapperId"`
	CollaboratorIds *string `json:"collaboratorIds"`
	CoverImage      string  `json:"coverImage"`
	Bpm             float64 `json:"bpm"`
	Duration        float64 `json:"duration"`
	FullCoverImage  *string `json:"fullCoverImage"`
}

type Difficulty struct {
	ID             int            `json:"id"`
	Value          int            `json:"value"`
	Mode           int            `json:"mode"`
	DifficultyName string         `json:"difficultyName"`
	ModeName       string         `json:"modeName"`
	Status         int            `json:"status"`
	ModifierValues ModifierValues `json:"modifierValues"`
	// ModifiersRating *string        `json:"modifiersRating"`
	NominatedTime int64    `json:"nominatedTime"`
	QualifiedTime int64    `json:"qualifiedTime"`
	RankedTime    int64    `json:"rankedTime"`
	SpeedTags     int      `json:"speedTags"`
	StyleTags     int      `json:"styleTags"`
	FeatureTags   int      `json:"featureTags"`
	Stars         *float64 `json:"stars"`
	PredictedAcc  float64  `json:"predictedAcc"`
}

type ModifierValues struct {
	ModifierID int     `json:"modifierId"`
	Da         float64 `json:"da"`
	Fs         float64 `json:"fs"`
	Sf         float64 `json:"sf"`
	// 其他修改器字段...
}

type Player struct {
	ID          string  `json:"id"`
	Name        string  `json:"name"`
	Platform    string  `json:"platform"`
	Avatar      string  `json:"avatar"`
	Country     string  `json:"country"`
	Alias       *string `json:"alias"`
	Bot         bool    `json:"bot"`
	Pp          float64 `json:"pp"`
	Rank        int     `json:"rank"`
	CountryRank int     `json:"countryRank"`
	Role        string  `json:"role"`
	// Socials           *string `json:"socials"`
	ContextExtensions *string `json:"contextExtensions"`
}

type Offsets struct {
	ID      int `json:"id"`
	Frames  int `json:"frames"`
	Notes   int `json:"notes"`
	Walls   int `json:"walls"`
	Heights int `json:"heights"`
	Pauses  int `json:"pauses"`
}

// 表示记录的数据，本地储存
type RecordDataLite struct {
	ID                    int     `json:"id" db:"id"`
	ScoreID               int     `json:"scoreId" db:"score_id"`
	BlID                  string  `json:"blId" db:"bl_id"`
	Name                  string  `json:"name" db:"name"`
	Country               string  `json:"country" db:"country"`
	SongName              string  `json:"songName" db:"song_name"`
	SongSubName           string  `json:"songSubName" db:"song_sub_name"`
	SongAuthorName        string  `json:"songAuthorName" db:"song_author_name"`
	SongHash              string  `json:"songHash" db:"song_hash"`
	SongId                string  `json:"songId" db:"song_id"`
	CoverImage            string  `json:"coverImage" db:"cover_image"`
	DifficultyRaw         string  `json:"difficultyRaw" db:"difficulty_raw"`
	Stars                 float64 `json:"stars" db:"stars"`
	PP                    float64 `json:"pp" db:"pp"`
	Weight                float64 `json:"weight" db:"weight"`
	Modifiers             string  `json:"modifiers" db:"modifiers"`
	Multiplier            float64 `json:"multiplier" db:"multiplier"`
	Rank                  int     `json:"rank" db:"rank"`
	BadCuts               int     `json:"badCuts" db:"bad_cuts"`
	MissedNotes           int     `json:"missedNotes" db:"missed_notes"`
	MaxCombo              int     `json:"maxCombo" db:"max_combo"`
	Score                 int     `json:"score" db:"score"`
	MaxScore              int     `json:"maxScore" db:"max_score"`
	FullCombo             bool    `json:"fullCombo" db:"full_combo"`
	DeviceHmd             string  `json:"deviceHmd" db:"device_hmd"`
	DeviceControllerLeft  string  `json:"deviceControllerLeft" db:"device_controller_left"`
	DeviceControllerRight string  `json:"deviceControllerRight" db:"device_controller_right"`
	GeneratedTime         string  `json:"generatedTime" db:"generated_time"`
}

func (r RecordDataLite) ToString() string {
	formatedStrRanked := "%s，%s 使用 %s 在 %s(%s) 的 %s 难度(%.1f🌟)中打到了全球排名第%d，pp 为 %.2f，准度为 %s。"
	formatedStrUnranked := "%s, %s 使用 %s 在 %s(%s) 的 %s 难度中打到了全球排名第%d，准度为 %s。"
	formatedStrWithoutDevice := "%s, %s 在 %s(%s) 的 %s 难度(%.1f🌟)中打到了全球排名第%d，pp 为 %.2f，准度为 %s。"
	formatedStrWithoutDeviceAndRank := "%s, %s 在 %s(%s) 的 %s 难度中打到了全球排名第%d，准度为 %s。"
	hardStr := r.DifficultyRaw
	layout := "2006-01-02 15:04:05.999999999-07:00"
	parsedTime, _ := time.Parse(layout, r.GeneratedTime)
	duration := time.Since(parsedTime)
	timeStr := timeConvert(duration)
	if r.Stars == 0 && r.DeviceHmd != "" {
		return fmt.Sprintf(formatedStrUnranked, timeStr, r.Name, r.DeviceHmd, r.SongName, r.SongId, hardStr, r.Rank, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100))
	} else if r.Stars != 0 && r.DeviceHmd != "" {
		return fmt.Sprintf(formatedStrRanked, timeStr, r.Name, r.DeviceHmd, r.SongName, r.SongId, hardStr, r.Stars, r.Rank, r.PP, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100))
	} else if r.Stars != 0 && r.DeviceHmd == "" {
		return fmt.Sprintf(formatedStrWithoutDevice, timeStr, r.Name, r.SongName, r.SongId, hardStr, r.Stars, r.Rank, r.PP, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100))
	} else {
		return fmt.Sprintf(formatedStrWithoutDeviceAndRank, timeStr, r.Name, r.SongName, r.SongId, hardStr, r.Rank, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100))
	}
}

func timeConvert(duration time.Duration) string {
	var result string
	if duration.Hours() >= 24 {
		days := int(duration.Hours() / 24)
		result = fmt.Sprintf("%d天前", days)
	} else if duration.Hours() >= 1 {
		hours := int(duration.Hours())
		result = fmt.Sprintf("%d小时前", hours)
	} else if duration.Minutes() >= 1 {
		minutes := int(duration.Minutes())
		result = fmt.Sprintf("%d分钟前", minutes)
	} else {
		result = "刚刚"
	}
	return result
}

//用户信息

// ScoreStats 存储分数统计信息
type PlayerData struct {
	MapperID int  `json:"mapperId"`
	Banned   bool `json:"banned"`
	Inactive bool `json:"inactive"`
	// BanDescription     *string   `json:"banDescription"`
	ExternalProfileURL string    `json:"externalProfileUrl"`
	RichBioTimeset     int64     `json:"richBioTimeset"`
	SpeedrunStart      int64     `json:"speedrunStart"`
	LinkedIDs          LinkedIDs `json:"linkedIds"`
	// History             *string    `json:"history"`
	// Badges              []string   `json:"badges"`
	// PinnedScores        *string    `json:"pinnedScores"`
	// Changes             []Change   `json:"changes"`
	AccPp               float64    `json:"accPp"`
	PassPp              float64    `json:"passPp"`
	TechPp              float64    `json:"techPp"`
	ScoreStats          ScoreStats `json:"scoreStats"`
	LastWeekPp          float64    `json:"lastWeekPp"`
	LastWeekRank        int        `json:"lastWeekRank"`
	LastWeekCountryRank int        `json:"lastWeekCountryRank"`
	ExtensionID         int        `json:"extensionId"`
	ID                  string     `json:"id"`
	Name                string     `json:"name"`
	Platform            string     `json:"platform"`
	Avatar              string     `json:"avatar"`
	Country             string     `json:"country"`
	// Alias               *string    `json:"alias"`
	Bot         bool    `json:"bot"`
	PP          float64 `json:"pp"`
	Rank        int     `json:"rank"`
	CountryRank int     `json:"countryRank"`
	Role        string  `json:"role"`
	// Socials             []string        `json:"socials"`
	// ContextExtensions *string         `json:"contextExtensions"`
	// PatreonFeatures   *string         `json:"patreonFeatures"`
	// ProfileSettings   ProfileSettings `json:"profileSettings"`
	// ClanOrder         string          `json:"clanOrder"`
	// Clans             []string        `json:"clans"`
}

func (p PlayerData) ToDataLite() PlayerDataLite {
	dataLite := PlayerDataLite{
		ID:                    p.ID,
		Name:                  p.Name,
		Country:               p.Country,
		Avatar:                p.Avatar,
		PP:                    p.AccPp + p.PassPp + p.TechPp,
		Rank:                  p.Rank,
		CountryRank:           p.CountryRank,
		TotalScore:            p.ScoreStats.TotalScore,
		TotalRankedScore:      p.ScoreStats.TotalRankedScore,
		AverageRankedAccuracy: p.ScoreStats.AverageRankedAccuracy,
		TotalPlayCount:        p.ScoreStats.TotalPlayCount,
		RankedPlayCount:       p.ScoreStats.RankedPlayCount,
		ReplaysWatched:        p.ScoreStats.WatchedReplays,
		GeneratedTime:         time.Now().Format("2006-01-02 15:04:05.999999999-07:00"),
	}
	return dataLite
}

type LinkedIDs struct {
	QuestID    int    `json:"questId"`
	SteamID    string `json:"steamId"`
	OculusPCID string `json:"oculusPCId"`
}

type Change struct {
	ID         int64   `json:"id"`
	Timestamp  int64   `json:"timestamp"`
	PlayerID   string  `json:"playerId"`
	OldName    string  `json:"oldName"`
	NewName    string  `json:"newName"`
	OldCountry string  `json:"oldCountry"`
	NewCountry string  `json:"newCountry"`
	Changer    *string `json:"changer"`
}

type ScoreStats struct {
	ID                            int64   `json:"id"`
	TotalScore                    int64   `json:"totalScore"`
	TotalUnrankedScore            int64   `json:"totalUnrankedScore"`
	TotalRankedScore              int64   `json:"totalRankedScore"`
	LastScoreTime                 int64   `json:"lastScoreTime"`
	LastUnrankedScoreTime         int64   `json:"lastUnrankedScoreTime"`
	LastRankedScoreTime           int64   `json:"lastRankedScoreTime"`
	AverageRankedAccuracy         float64 `json:"averageRankedAccuracy"`
	AverageWeightedRankedAccuracy float64 `json:"averageWeightedRankedAccuracy"`
	AverageUnrankedAccuracy       float64 `json:"averageUnrankedAccuracy"`
	AverageAccuracy               float64 `json:"averageAccuracy"`
	MedianRankedAccuracy          float64 `json:"medianRankedAccuracy"`
	MedianAccuracy                float64 `json:"medianAccuracy"`
	TopRankedAccuracy             float64 `json:"topRankedAccuracy"`
	TopUnrankedAccuracy           float64 `json:"topUnrankedAccuracy"`
	TopAccuracy                   float64 `json:"topAccuracy"`
	TopPp                         float64 `json:"topPp"`
	TopBonusPP                    float64 `json:"topBonusPP"`
	TopPassPP                     float64 `json:"topPassPP"`
	TopAccPP                      float64 `json:"topAccPP"`
	TopTechPP                     float64 `json:"topTechPP"`
	PeakRank                      int     `json:"peakRank"`
	RankedMaxStreak               int     `json:"rankedMaxStreak"`
	UnrankedMaxStreak             int     `json:"unrankedMaxStreak"`
	MaxStreak                     int     `json:"maxStreak"`
	AverageLeftTiming             float64 `json:"averageLeftTiming"`
	AverageRightTiming            float64 `json:"averageRightTiming"`
	RankedPlayCount               int     `json:"rankedPlayCount"`
	UnrankedPlayCount             int     `json:"unrankedPlayCount"`
	TotalPlayCount                int     `json:"totalPlayCount"`
	RankedImprovementsCount       int     `json:"rankedImprovementsCount"`
	UnrankedImprovementsCount     int     `json:"unrankedImprovementsCount"`
	TotalImprovementsCount        int     `json:"totalImprovementsCount"`
	RankedTop1Count               int     `json:"rankedTop1Count"`
	UnrankedTop1Count             int     `json:"unrankedTop1Count"`
	Top1Count                     int     `json:"top1Count"`
	RankedTop1Score               int64   `json:"rankedTop1Score"`
	UnrankedTop1Score             int64   `json:"unrankedTop1Score"`
	Top1Score                     int64   `json:"top1Score"`
	AverageRankedRank             float64 `json:"averageRankedRank"`
	AverageWeightedRankedRank     float64 `json:"averageWeightedRankedRank"`
	AverageUnrankedRank           float64 `json:"averageUnrankedRank"`
	AverageRank                   float64 `json:"averageRank"`
	SspPlays                      int     `json:"sspPlays"`
	SsPlays                       int     `json:"ssPlays"`
	SpPlays                       int     `json:"spPlays"`
	SPlays                        int     `json:"sPlays"`
	APlays                        int     `json:"aPlays"`
	TopPlatform                   string  `json:"topPlatform"`
	TopHMD                        int     `json:"topHMD"`
	AllHMDs                       string  `json:"allHMDs"`
	TopPercentile                 float64 `json:"topPercentile"`
	CountryTopPercentile          float64 `json:"countryTopPercentile"`
	DailyImprovements             int     `json:"dailyImprovements"`
	AuthorizedReplayWatched       int     `json:"authorizedReplayWatched"`
	AnonimusReplayWatched         int     `json:"anonimusReplayWatched"`
	WatchedReplays                int     `json:"watchedReplays"`
}

type ProfileSettings struct {
	ID                    int64   `json:"id"`
	Bio                   *string `json:"bio"`
	Message               *string `json:"message"`
	EffectName            string  `json:"effectName"`
	ProfileAppearance     string  `json:"profileAppearance"`
	Hue                   int     `json:"hue"`
	Saturation            float64 `json:"saturation"`
	LeftSaberColor        *string `json:"leftSaberColor"`
	RightSaberColor       *string `json:"rightSaberColor"`
	ProfileCover          *string `json:"profileCover"`
	StarredFriends        string  `json:"starredFriends"`
	HorizontalRichBio     bool    `json:"horizontalRichBio"`
	RankedMapperSort      *string `json:"rankedMapperSort"`
	ShowBots              bool    `json:"showBots"`
	ShowAllRatings        bool    `json:"showAllRatings"`
	ShowStatsPublic       bool    `json:"showStatsPublic"`
	ShowStatsPublicPinned bool    `json:"showStatsPublicPinned"`
}

type PlayerDataLite struct {
	ID                    string  `json:"id" db:"id"`
	Name                  string  `json:"name" db:"name"`
	Country               string  `json:"country" db:"country"`
	Avatar                string  `json:"avatar" db:"avatar"`
	Device                string  `json:"device" db:"device"`
	PP                    float64 `json:"pp" db:"pp"`
	Rank                  int     `json:"rank" db:"rank"`
	CountryRank           int     `json:"countryRank" db:"country_rank"`
	TotalScore            int64   `json:"totalScore" db:"total_score"`
	TotalRankedScore      int64   `json:"totalRankedScore" db:"total_ranked_score"`
	AverageRankedAccuracy float64 `json:"averageRankedAccuracy" db:"average_ranked_accuracy"`
	TotalPlayCount        int     `json:"totalPlayCount" db:"total_play_count"`
	RankedPlayCount       int     `json:"rankedPlayCount" db:"ranked_play_count"`
	ReplaysWatched        int     `json:"replaysWatched" db:"replays_watched"`
	GeneratedTime         string  `json:"generatedTime" db:"generated_time"`
}

func (p PlayerDataLite) IsDiffFrom(p2 PlayerDataLite) bool {
	return p.TotalScore != p2.TotalScore ||
		p.TotalRankedScore != p2.TotalRankedScore ||
		p.AverageRankedAccuracy != p2.AverageRankedAccuracy ||
		p.TotalPlayCount != p2.TotalPlayCount ||
		p.RankedPlayCount != p2.RankedPlayCount ||
		p.ReplaysWatched != p2.ReplaysWatched
}

func (p PlayerDataLite) LastDiffToString(lastQueryData PlayerDataLite) string {
	filePath, err := util.DownloadFile(p.Avatar, "/tmp/qqbot", false)
	if err != nil {
		log.Default().Printf("下载头像失败，url:%s,err:%v", p.Avatar, err)
	}
	defer os.Remove(filePath)
	outFile, err := util.ResizeImageByMaxHeight2File(filePath, 20)
	if err != nil {
		log.Default().Printf("缩放头像失败，url:%s,err:%v", p.Avatar, err)
	}
	picMsg := qq_message.ImageMessage{
		Type: qq_message.TypeImage,
		Data: qq_message.ImageMessageData{
			File: outFile,
		},
	}

	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("玩家 %s\n", p.Name))
	sb.WriteString(picMsg.ToCQString())
	sb.WriteString(fmt.Sprintf("区域 %s\n", p.Country))

	// PP值
	ppDiff := p.PP - lastQueryData.PP
	if ppDiff == 0 {
		sb.WriteString(fmt.Sprintf("PP %.1f\n", p.PP))
	} else {
		sb.WriteString(fmt.Sprintf("PP %.1f(%+.1f)\n", p.PP, ppDiff))
	} // 全球排名
	rankDiff := lastQueryData.Rank - p.Rank
	if rankDiff == 0 {
		sb.WriteString(fmt.Sprintf("全球排名 %d\n", p.Rank))
	} else {
		sb.WriteString(fmt.Sprintf("全球排名 %d(%+d)\n", p.Rank, rankDiff))
	}

	// 区域排名
	countryRankDiff := lastQueryData.CountryRank - p.CountryRank
	if countryRankDiff == 0 {
		sb.WriteString(fmt.Sprintf("区域排名 %d\n", p.CountryRank))
	} else {
		sb.WriteString(fmt.Sprintf("区域排名 %d(%+d)\n", p.CountryRank, countryRankDiff))
	}

	// Ranked谱面均准
	accDiff := (p.AverageRankedAccuracy - lastQueryData.AverageRankedAccuracy) * 100
	if accDiff == 0 {
		sb.WriteString(fmt.Sprintf("Ranked谱面均准 %.2f%%\n", p.AverageRankedAccuracy*100))
	} else {
		sb.WriteString(fmt.Sprintf("Ranked谱面均准 %.2f%%(%+.2f%%)\n", p.AverageRankedAccuracy*100, accDiff))
	}
	// 总游玩记数
	totalPlayDiff := p.TotalPlayCount - lastQueryData.TotalPlayCount
	if totalPlayDiff == 0 {
		sb.WriteString(fmt.Sprintf("总游玩记数 %d\n", p.TotalPlayCount))
	} else {
		sb.WriteString(fmt.Sprintf("总游玩记数 %d(%+d)\n", p.TotalPlayCount, totalPlayDiff))
	}

	// Ranked谱面游玩记数
	rankedPlayDiff := p.RankedPlayCount - lastQueryData.RankedPlayCount
	if rankedPlayDiff == 0 {
		sb.WriteString(fmt.Sprintf("Ranked谱面游玩记数 %d\n", p.RankedPlayCount))
	} else {
		sb.WriteString(fmt.Sprintf("Ranked谱面游玩记数 %d(%+d)\n", p.RankedPlayCount, rankedPlayDiff))
	}
	// 回放被观看次数
	sb.WriteString(fmt.Sprintf("回放被观看次数 %d", p.ReplaysWatched))

	return sb.String()
}

func (p PlayerDataLite) LastDiffToImage(lastQueryData PlayerDataLite, onlyFirstFrame bool) string {
	filePath, err := util.DownloadFile(p.Avatar, "/tmp/qqbot", false)
	if err != nil {
		log.Default().Printf("下载头像失败，url:%s,err:%v", p.Avatar, err)
	}
	defer os.Remove(filePath)

	baseboard := sprite.NewNamedSpriteBoard()

	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("玩家 %s\n", p.Name))
	sb.WriteString(fmt.Sprintf("区域 %s\n", p.Country))
	{
		// PP值
		ppDiff := p.PP - lastQueryData.PP
		if ppDiff == 0 {
			sb.WriteString(fmt.Sprintf("PP %.1f\n", p.PP))
		} else {
			sb.WriteString(fmt.Sprintf("PP %.1f(%+.1f)\n", p.PP, ppDiff))
		} // 全球排名
		rankDiff := lastQueryData.Rank - p.Rank
		if rankDiff == 0 {
			sb.WriteString(fmt.Sprintf("全球排名 %d\n", p.Rank))
		} else {
			sb.WriteString(fmt.Sprintf("全球排名 %d(%+d)\n", p.Rank, rankDiff))
		}

		// 区域排名
		countryRankDiff := lastQueryData.CountryRank - p.CountryRank
		if countryRankDiff == 0 {
			sb.WriteString(fmt.Sprintf("区域排名 %d\n", p.CountryRank))
		} else {
			sb.WriteString(fmt.Sprintf("区域排名 %d(%+d)\n", p.CountryRank, countryRankDiff))
		}

		// Ranked谱面均准
		accDiff := (p.AverageRankedAccuracy - lastQueryData.AverageRankedAccuracy) * 100
		if accDiff == 0 {
			sb.WriteString(fmt.Sprintf("Ranked谱面均准 %.2f%%\n", p.AverageRankedAccuracy*100))
		} else {
			sb.WriteString(fmt.Sprintf("Ranked谱面均准 %.2f%%(%+.2f%%)\n", p.AverageRankedAccuracy*100, accDiff))
		}
		// 总游玩记数
		totalPlayDiff := p.TotalPlayCount - lastQueryData.TotalPlayCount
		if totalPlayDiff == 0 {
			sb.WriteString(fmt.Sprintf("总游玩记数 %d\n", p.TotalPlayCount))
		} else {
			sb.WriteString(fmt.Sprintf("总游玩记数 %d(%+d)\n", p.TotalPlayCount, totalPlayDiff))
		}

		// Ranked谱面游玩记数
		rankedPlayDiff := p.RankedPlayCount - lastQueryData.RankedPlayCount
		if rankedPlayDiff == 0 {
			sb.WriteString(fmt.Sprintf("Ranked谱面游玩记数 %d\n", p.RankedPlayCount))
		} else {
			sb.WriteString(fmt.Sprintf("Ranked谱面游玩记数 %d(%+d)\n", p.RankedPlayCount, rankedPlayDiff))
		}
		// 回放被观看次数
		sb.WriteString(fmt.Sprintf("回放被观看次数 %d", p.ReplaysWatched))
	}
	text := sb.String()
	textImg, err := text2img.RenderTextToTrimmedImage(nil, text, 24, color.Black, 0, 0)
	if err != nil {
		log.Default().Printf("渲染文字失败，err:%v", err)
	}

	avatar, delay, err := util.ResizeImageByMaxHeight2Image(filePath, uint(textImg.Bounds().Dy()))
	if err != nil {
		log.Default().Printf("缩放头像失败，url:%s,err:%v", p.Avatar, err)
	}
	avatarSpirit := sprite.Sprite{
		Name:   "avatar",
		Images: avatar,
		Delay:  delay,
		Index:  1,
	}
	if avatar == nil {
		avatarSpirit.Images = []image.Image{image.NewRGBA(image.Rect(0, 0, 0, 0))}
	}
	baseboard.AddSprite(&avatarSpirit)

	textSpirit := sprite.Sprite{
		Name:     "text",
		Images:   []image.Image{textImg},
		Index:    2,
		Position: image.Point{X: avatarSpirit.Position.X + avatarSpirit.Images[0].Bounds().Dx() + 3, Y: 0},
	}
	baseboard.AddSprite(&textSpirit)

	minX, minY, maxX, maxY := baseboard.GetRenderBounds()
	totalWidth := maxX - minX
	totalHeight := maxY - minY

	background := image.NewRGBA(image.Rect(0, 0, int(totalWidth+10), int(totalHeight+10)))
	draw.Draw(background, background.Bounds(), image.White, image.Point{}, draw.Src)
	backgroundSpirit := sprite.Sprite{
		Name:     "background",
		Images:   []image.Image{background},
		Index:    0,
		Position: image.Point{X: minX - 5, Y: minY - 5},
	}
	baseboard.AddSprite(&backgroundSpirit)
	if len(avatarSpirit.Images) > 1 && !onlyFirstFrame {
		if err := baseboard.SaveToGif(util.GenTempFilePath("cbl.gif")); err != nil {
			log.Default().Printf("保存图片失败，err:%v", err)
		}
		return util.GenTempFilePath("cbl.gif")
	}
	if err := baseboard.SaveToApng(util.GenTempFilePath("cbl.png")); err != nil {
		log.Default().Printf("保存图片失败，err:%v", err)
	}
	return util.GenTempFilePath("cbl.png")
}

func GetControllerStr(controller int) string {
	switch controller {
	case 1:
		return "Oculus Touch 手柄"
	case 16:
		return "Oculus Touch 2 手柄"
	case 79:
		return "Meta Quest 3 手柄"
	case 256:
		return "Quest 2 手柄"
	case 2:
		return "VIVE wands"
	case 4:
		return "VIVE 2 wands"
	case 128:
		return "VIVE cosmos controllers"
	case 8:
		return "WMR 手柄"
	case 33:
		return "Pico 手柄"
	case 34:
		return "Pico 手柄"
	case 35:
		return "VIVE Pro dudads"
	case 37:
		return "Miramar 手柄"
	case 44:
		return "disco 手柄"
	case 61:
		return "Quest Pro 手柄"
	case 62:
		return "VIVE tracker"
	case 63:
		return "VIVE tracker 2"
	case 64:
		return "Knuckles"
	case 65:
		return "nolo手柄"
	case 66:
		return "Pico phoenix"
	case 67:
		return "双手🙌"
	case 68:
		return "VIVE tracker 3"
	case 75:
		return "游戏手柄🎮"
	case 76:
		return "Joy-Con"
	case 77:
		return "Steam Deck"
	case 78:
		return "Etee"
	}
	return "神秘手柄"
}

func GetHMDStr(hmd int) string {
	switch hmd {
	case 256:
		return "Quest 2"
	case 512:
		return "Quest 3"
	case 64:
		return "Valve Index"
	case 513:
		return "Quest 3S"
	case 1:
		return "Rift CV1"
	case 2:
		return "Vive"
	case 60:
		return "Pico 4"
	case 61:
		return "Quest Pro"
	case 70:
		return "PS VR2"
	case 8:
		return "WMR"
	case 16:
		return "Rift S"
	case 65:
		return "Controllable"
	case 32:
		return "Quest"
	case 4:
		return "Vive Pro"
	case 35:
		return "Vive Pro 2"
	case 128:
		return "Vive Cosmos"
	case 36:
		return "Vive Elite"
	case 47:
		return "Vive Focus"
	case 38:
		return "Pimax 8K"
	case 39:
		return "Pimax 5K"
	case 40:
		return "Pimax Artisan"
	case 33:
		return "Pico Neo 3"
	case 34:
		return "Pico Neo 2"
	case 41:
		return "HP Reverb"
	case 42:
		return "Samsung WMR"
	case 43:
		return "Qiyu Dream"
	case 45:
		return "Lenovo Explorer"
	case 46:
		return "Acer WMR"
	case 66:
		return "Bigscreen Beyond"
	case 67:
		return "NOLO Sonic"
	case 68:
		return "Hypereal"
	case 48:
		return "Arpara"
	case 49:
		return "Dell Visor"
	case 71:
		return "MeganeX VG1"
	case 55:
		return "Huawei VR"
	case 56:
		return "Asus WMR"
	case 51:
		return "Vive DVT"
	case 52:
		return "glasses20"
	case 53:
		return "Varjo"
	case 69:
		return "Varjo Aero"
	case 54:
		return "Vaporeon"
	case 57:
		return "Cloud XR"
	case 58:
		return "VRidge"
	case 50:
		return "e3"
	case 59:
		return "Medion Eraser"
	case 37:
		return "Miramar"
	case 0:
		return "Unknown headset"
	case 44:
		return "Disco"
	default:
		return "神秘头显"
	}
}
