package jobs

import (
	"encoding/json"
	"fmt"
	"gadmin/config"
	"gadmin/internal/gorm/model"
	"gadmin/internal/gorm/query"
	"os"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
)

var SyncChapter = new(jSyncChapter)

type jSyncChapter struct {
	sync.Mutex
}

type chapterItem struct {
	Id         int64  `json:"id"`
	UserId     int64  `json:"user_id"`
	ChapterId  int32  `json:"chapter_id"`
	Extra      string `json:"extra"`
	EventAt    int64  `json:"event_at"`
	ServerID   int32  `json:"server_id"` // 服务器ID
	Difficulty int32  `json:"difficulty"`
}

// Run
// 目前只统计100~103活动章节
func (j *jSyncChapter) Run() {
	logrus.Info("jSyncChapter Run.....")

	if os.Getenv("GIN_MODE") != "release" && os.Getenv("ADMIN_IS_LOCAL") != "1" {
		logrus.Warnf("测试环境禁止同步")
		return
	}

	j.Lock()
	defer j.Unlock()

	chapterIds := []int64{100, 101, 102, 103}
	var difficultys = []int32{0, 1, 2}
	for serverId := range config.GDBGroup {
		for _, chapterId := range chapterIds {
			if chapterId == 103 {
				j.syncChapter(serverId, chapterId, 0)
				continue
			}

			for _, difficulty := range difficultys {
				j.syncChapter(serverId, chapterId, difficulty)
			}
		}
	}

}

func (j *jSyncChapter) syncChapter(serverId int, chapterId int64, difficulty int32) {
	logrus.Infof("load SyncChapter:%v, difficulty:%v", chapterId, difficulty)

	lastSyncInfo, err := j.getLastId(serverId, chapterId, difficulty)
	if err != nil {
		logrus.Warnf("getLastId err . %v", err)
		return
	}

	var (
		sql        = ""
		chapterLst []chapterItem
	)

	sql = fmt.Sprintf("SELECT id,user_id,chapter_id,extra,event_at,server_id,difficulty FROM `chapter_logs_%d` WHERE `id` > %d and server_id = %v and difficulty = %v",
		chapterId, lastSyncInfo.LastID, serverId, difficulty) +
		" AND `user_id` > 0 AND `event_id` = 11" // AND (JSON_CONTAINS(extra, '\"type\"') = 1)
	config.DB.Raw(sql).Scan(&chapterLst)

	if len(chapterLst) == 0 {
		logrus.Warnf("没有需要同步的章节信息")
		return
	}

	for _, chapter := range chapterLst {
		if err = j.saveChapter(chapter); err != nil {
			logrus.Warnf("saveChapter err :%+v", err)
		}
	}

	if err = j.setLastId(serverId, chapterId, difficulty, chapterLst[len(chapterLst)-1], lastSyncInfo.ID); err != nil {
		logrus.Warnf("setLastId err :%+v", err)
	}
}

func (j *jSyncChapter) saveChapter(data chapterItem) (err error) {
	var (
		models model.Chapter
		q            = query.Use(config.DB).Chapter
		suc    int32 = 0
		fail   int32 = 0
		t            = time.Now()
	)

	if j.playerPass(data.Extra) {
		suc = 1
	} else {
		fail = 1
	}

	if err = q.Where(q.PlayerID.Eq(data.UserId), q.ChapterID.Eq(data.ChapterId), q.Difficulty.Eq(data.Difficulty)).Scan(&models); err != nil {
		return err
	}

	if models.ID > 0 {
		if _, err = query.Use(config.DB).Chapter.Where(q.ID.Eq(models.ID)).UpdateSimple(q.ClearanceCount.Add(suc), q.FailCount.Add(fail), q.UpdatedAt.Value(t)); err != nil {
			return err
		}
		return nil
	} else {
		return query.Use(config.DB).Chapter.Create(&model.Chapter{
			ServerID:       data.ServerID,
			PlayerID:       data.UserId,
			ChapterID:      data.ChapterId,
			Difficulty:     data.Difficulty,
			ClearanceCount: suc,
			FailCount:      fail,
			CreatedAt:      t,
			UpdatedAt:      t,
		})
	}
}

func (j *jSyncChapter) playerPass(extraInfo string) bool {
	type passType struct {
		Type int `json:"type"` //-1死亡结算；主动退出结算 1通关结算
	}
	var passTypeTemp passType
	json.Unmarshal([]byte(extraInfo), &passTypeTemp)
	return passTypeTemp.Type == 1
}

func (j *jSyncChapter) getLastId(serverId int, chapterId int64, difficulty int32) (res model.ChapterSync, err error) {
	var (
		q      = query.Use(config.DB).ChapterSync
		models model.ChapterSync
	)

	if err = q.Where(q.ChapterID.Eq(chapterId), q.ServerID.Eq(int32(serverId)), q.Difficulty.Eq(difficulty)).Scan(&models); err != nil {
		return
	}
	return models, nil
}

func (j *jSyncChapter) setLastId(serverId int, chapterId int64, difficulty int32, data chapterItem, lastSyncInfoId int64) (err error) {
	var (
		q = query.Use(config.DB).ChapterSync
	)
	syncData := &model.ChapterSync{
		ChapterID:    chapterId,
		ServerID:     int32(serverId),
		Difficulty:   difficulty,
		LastID:       data.Id,
		LastSyncTime: time.Unix(data.EventAt, 0),
		UpdatedAt:    time.Now(),
	}
	if lastSyncInfoId != 0 {
		syncData.ID = lastSyncInfoId
	}

	if err = q.Where(q.ChapterID.Eq(chapterId), q.ServerID.Eq(int32(serverId)), q.Difficulty.Eq(difficulty)).Save(syncData); err != nil {
		return err
	}
	return
}
