package scoresaber

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"
)

// Command 表示从 WebSocket 收到的数据结构
type Command struct {
	CommandName string      `json:"commandName"`
	CommandData CommandData `json:"commandData"`
}

// type Badge struct {
// 	Description string `json:"description"`
// 	Image       string `json:"image"`
// }

// LeaderboardPlayerInfo 表示玩家的信息
type LeaderboardPlayerInfo struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	ProfilePicture string `json:"profilePicture"`
	Country        string `json:"country"`
	Permissions    int    `json:"permissions"`
}

// Score 表示分数的信息
type Score struct {
	ID                    int                   `json:"id"`
	LeaderboardPlayerInfo LeaderboardPlayerInfo `json:"leaderboardPlayerInfo"`
	Rank                  int                   `json:"rank"`
	BaseScore             int                   `json:"baseScore"`
	ModifiedScore         int                   `json:"modifiedScore"`
	Pp                    float64               `json:"pp"`
	Weight                float64               `json:"weight"`
	Modifiers             string                `json:"modifiers"`
	Multiplier            float64               `json:"multiplier"`
	BadCuts               int                   `json:"badCuts"`
	MissedNotes           int                   `json:"missedNotes"`
	MaxCombo              int                   `json:"maxCombo"`
	FullCombo             bool                  `json:"fullCombo"`
	Hmd                   int                   `json:"hmd"`
	TimeSet               time.Time             `json:"timeSet"`
	HasReplay             bool                  `json:"hasReplay"`
	DeviceHmd             *string               `json:"deviceHmd"`
	DeviceControllerLeft  *string               `json:"deviceControllerLeft"`
	DeviceControllerRight *string               `json:"deviceControllerRight"`
}

// Difficulty 表示关卡难度信息
type Difficulty struct {
	LeaderboardID int    `json:"leaderboardId"`
	Difficulty    int    `json:"difficulty"`
	GameMode      string `json:"gameMode"`
	DifficultyRaw string `json:"difficultyRaw"`
}

// Leaderboard 表示排行榜的信息
type Leaderboard struct {
	ID                int         `json:"id"`
	SongHash          string      `json:"songHash"`
	SongName          string      `json:"songName"`
	SongSubName       string      `json:"songSubName"`
	SongAuthorName    string      `json:"songAuthorName"`
	LevelAuthorName   string      `json:"levelAuthorName"`
	Difficulty        Difficulty  `json:"difficulty"`
	MaxScore          int         `json:"maxScore"`
	CreatedDate       time.Time   `json:"createdDate"`
	RankedDate        *time.Time  `json:"rankedDate"`
	QualifiedDate     *time.Time  `json:"qualifiedDate"`
	LovedDate         *time.Time  `json:"lovedDate"`
	Ranked            bool        `json:"ranked"`
	Qualified         bool        `json:"qualified"`
	Loved             bool        `json:"loved"`
	MaxPP             float64     `json:"maxPP"`
	Stars             float64     `json:"stars"`
	Plays             int         `json:"plays"`
	DailyPlays        int         `json:"dailyPlays"`
	PositiveModifiers bool        `json:"positiveModifiers"`
	PlayerScore       *string     `json:"playerScore"`
	CoverImage        string      `json:"coverImage"`
	Difficulties      interface{} `json:"difficulties"`
}

// CommandData 表示命令的数据
type CommandData struct {
	Score       Score       `json:"score"`
	Leaderboard Leaderboard `json:"leaderboard"`
}

// 表示记录的数据，本地储存
type RecordDataLite struct {
	ID                    int     `json:"id" db:"id"`
	ScoreID               int     `json:"scoreId" db:"score_id"`
	SsID                  string  `json:"ssId" db:"ss_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 := strings.Split(r.DifficultyRaw, "_")[1]
	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 ScoreStats struct {
	TotalScore            int     `json:"totalScore" db:"total_score"`
	TotalRankedScore      int     `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"`
}

// PlayerData 存储玩家的完整信息
type PlayerData struct {
	ID             string  `json:"id" db:"id"`
	Name           string  `json:"name" db:"name"`
	ProfilePicture string  `json:"profilePicture" db:"profile_picture"`
	Bio            *string `json:"bio" db:"bio"`
	Country        string  `json:"country" db:"country"`
	PP             float64 `json:"pp" db:"pp"`
	Rank           int     `json:"rank" db:"rank"`
	CountryRank    int     `json:"countryRank" db:"country_rank"`
	Role           *string `json:"role" db:"role"`
	// Badges         []string   `json:"badges" db:"badges"`
	Histories   string     `json:"histories" db:"histories"`
	Permissions int        `json:"permissions" db:"permissions"`
	Banned      bool       `json:"banned" db:"banned"`
	Inactive    bool       `json:"inactive" db:"inactive"`
	ScoreStats  ScoreStats `json:"scoreStats" db:"score_stats"`
	FirstSeen   time.Time  `json:"firstSeen" db:"first_seen"`
}

type PlayerDataLite struct {
	ID                    string  `json:"id" db:"id"`
	Name                  string  `json:"name" db:"name"`
	ProfilePicture        string  `json:"profilePicture" db:"profile_picture"`
	Country               string  `json:"country" db:"country"`
	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            int     `json:"totalScore" db:"total_score"`
	TotalRankedScore      int     `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 PlayerData) ToString() string {
	filePath, err := util.DownloadFile(p.ProfilePicture, "/tmp/qqbot", false)
	if err != nil {
		log.Default().Printf("下载头像失败，url:%s,err:%v", p.ProfilePicture, err)
	}
	defer os.Remove(filePath)
	outFile, err := util.ResizeImageByMaxHeight2File(filePath, 20)
	if err != nil {
		log.Default().Printf("缩放头像失败，url:%s,err:%v", p.ProfilePicture, err)
	}
	picMsg := qq_message.ImageMessage{
		Type: qq_message.TypeImage,
		Data: qq_message.ImageMessageData{
			File: outFile,
		},
	}
	formatedStr := "玩家 %s\n" +
		picMsg.ToCQString() +
		"区域 %s\n" +
		"PP %.1f\n" +
		"全球排名 %d\n" +
		"区域排名 %d\n" +
		"Ranked谱面均准 %.2f%%\n" +
		"总游玩记数 %d\n" +
		"Ranked谱面游玩记数 %d\n" +
		"回放被观看次数 %d"
	return fmt.Sprintf(formatedStr,
		p.Name,
		p.Country,
		p.PP,
		p.Rank,
		p.CountryRank,
		p.ScoreStats.AverageRankedAccuracy,
		p.ScoreStats.TotalPlayCount,
		p.ScoreStats.RankedPlayCount,
		p.ScoreStats.ReplaysWatched)
}

func (p PlayerData) ToDataLite() PlayerDataLite {
	return PlayerDataLite{
		ID:                    p.ID,
		Name:                  p.Name,
		ProfilePicture:        p.ProfilePicture,
		Country:               p.Country,
		PP:                    p.PP,
		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.ReplaysWatched,
		GeneratedTime:         p.FirstSeen.Format(time.RFC3339),
	}
}

func (p PlayerData) LastDiffToString(lastDayQueryData PlayerDataLite) string {
	filePath, err := util.DownloadFile(p.ProfilePicture, "/tmp/qqbot", false)
	if err != nil {
		log.Default().Printf("下载头像失败，url:%s,err:%v", p.ProfilePicture, err)
	}
	defer os.Remove(filePath)
	outFile, err := util.ResizeImageByMaxHeight2File(filePath, 20)
	if err != nil {
		log.Default().Printf("缩放头像失败，url:%s,err:%v", p.ProfilePicture, err)
	}
	picMsg := qq_message.ImageMessage{
		Type: qq_message.TypeImage,
		Data: qq_message.ImageMessageData{
			File: outFile,
		},
	}
	formatedStr := "玩家 %s\n" +
		picMsg.ToCQString() +
		"区域 %s\n" +
		"PP %.1f(%+.1f)\n" +
		"全球排名 %d(%+d)\n" +
		"区域排名 %d(%+d)\n" +
		"Ranked谱面均准 %.2f%%(%+.2f%%)\n" +
		"总游玩记数 %d(%+d)\n" +
		"Ranked谱面游玩记数 %d(%+d)\n" +
		"回放被观看次数 %d"
	return fmt.Sprintf(formatedStr,
		p.Name,
		p.Country,
		p.PP, p.PP-lastDayQueryData.PP,
		p.Rank, lastDayQueryData.Rank-p.Rank,
		p.CountryRank, lastDayQueryData.CountryRank-p.CountryRank,
		p.ScoreStats.AverageRankedAccuracy, p.ScoreStats.AverageRankedAccuracy-lastDayQueryData.AverageRankedAccuracy,
		p.ScoreStats.TotalPlayCount, p.ScoreStats.TotalPlayCount-lastDayQueryData.TotalPlayCount,
		p.ScoreStats.RankedPlayCount, p.ScoreStats.RankedPlayCount-lastDayQueryData.RankedPlayCount,
		p.ScoreStats.ReplaysWatched)
}

func (p PlayerDataLite) LastDiffToImage(lastQueryData PlayerDataLite, onlyFirstFrame bool) string {
	filePath, err := util.DownloadFile(p.ProfilePicture, "/tmp/qqbot", false)
	if err != nil {
		log.Default().Printf("下载头像失败，url:%s,err:%v", p.ProfilePicture, 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))
		} else {
			sb.WriteString(fmt.Sprintf("Ranked谱面均准 %.2f%%(%+.2f%%)\n", p.AverageRankedAccuracy, 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.ProfilePicture, 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("css.gif")); err != nil {
			log.Default().Printf("保存图片失败，err:%v", err)
		}
		return util.GenTempFilePath("css.gif")
	}
	if err := baseboard.SaveToApng(util.GenTempFilePath("css.png")); err != nil {
		log.Default().Printf("保存图片失败，err:%v", err)
	}
	return util.GenTempFilePath("css.png")
}
