package player

import (
	"context"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"github.com/go-redis/redis/v8"
	jsoniter "github.com/json-iterator/go"
	mhayaString "github.com/mhaya/extend/string"
	mhayaTime "github.com/mhaya/extend/time"
	cfacade "github.com/mhaya/facade"
	"github.com/mhaya/game/game_cluster/internal/code"
	"github.com/mhaya/game/game_cluster/internal/constant"
	"github.com/mhaya/game/game_cluster/internal/data"
	"github.com/mhaya/game/game_cluster/internal/mdb"
	"github.com/mhaya/game/game_cluster/internal/mdb/models"
	"github.com/mhaya/game/game_cluster/internal/param"
	"github.com/mhaya/game/game_cluster/internal/token"
	"github.com/mhaya/game/game_cluster/nodes/game/module/online"
	clog "github.com/mhaya/logger"
	"github.com/mhaya/net/parser/pomelo"
	"go.mongodb.org/mongo-driver/bson"
	"io"
	"math/rand"
	"net/http"
	//"net/url"
	"time"
)

type (
	// actorPlayer 每位登录的玩家对应一个子actor
	actorPlayer struct {
		pomelo.ActorBase
		isOnline    bool // 玩家是否在线
		playerId    string
		uid         int64
		targetPath  string
		index       int
		dirty       bool
		exitTime    time.Duration
		Player      *models.Player
		Account     *models.Account
		TonWallFlag bool
	}

	CachePlayer struct {
		Player *models.Player
		Dirty  bool
	}
)

func (p *actorPlayer) OnInit() {
	p.exitTime = time.Duration(rand.Int63n(1200)) + time.Minute*10
	clog.Debugf("[actorPlayer] path = %s init!", p.PathString())

	// 注册 session关闭的remote函数(网关触发连接断开后，会调用RPC发送该消息)
	p.Remote().Register("sessionClose", p.sessionClose)

	p.Remote().Register("login", p.login) // 注册 查看角色

	p.Remote().Register("start", p.start) // 主页

	p.Remote().Register("guide", p.guide) // 主页关闭信息

	p.Remote().Register("roll", p.roll) // roll

	p.Remote().Register("lottery", p.lottery) // 转盘

	p.Remote().Register("signIn", p.signIn) // 签到

	p.Remote().Register("signInList", p.signInList) // 签到列表

	p.Remote().Register("rank", p.rank)               // 排行榜
	p.Remote().Register("countryRank", p.countryRank) // 国家排行榜

	p.Remote().Register("invite", p.invite)                                     // 邀请
	p.Remote().Register("inviteRecord", p.inviteRecord)                         // 邀请累计信息
	p.Remote().Register("inviteRewardRatio", p.inviteRewardRatio)               // 邀请裂变查询
	p.Remote().Register("inviteNodeReward", p.inviteNodeReward)                 // 邀请节点查询
	p.Remote().Register("itemWeekRecord", p.itemWeekRecord)                     // 收益流水
	p.Remote().Register("inviteRewardRatioHistory", p.inviteRewardRatioHistory) // 邀请奖励流水

	p.Remote().Register("share", p.share) // 分享

	p.Remote().Register("claim", p.claim) // 领取奖励

	p.Remote().Register("cashOut", p.cashOut) //提现

	p.Remote().Register("jump", p.jump)               // 成就1相关的跳转服务
	p.Remote().Register("savex", p.savex)             // 更新推特
	p.Remote().Register("savetonwall", p.savetonwall) // 更新钱包

	p.Remote().Register("setPwd", p.setPwd) // 设置密码

	p.Remote().Register("cashOutList", p.cashOutList) // 提现记录

	p.Remote().Register("getLotteryHistory", p.getLotteryHistory) // 抽奖记录

	p.Remote().Register("unitTest", p.unitTest)

	p.Remote().Register("setAvatar", p.setAvatar)

	//p.Timer().Add(10*time.Second, p.refreshRoll)
	//	p.Timer().Add(60*time.Second, p.SetRewardToDB)
	p.Timer().Add(1*time.Minute, p.setRedis)
	//p.Timer().Add(30*time.Second, p.setDb)
	//p.Timer().Add(30*time.Second, p.setInviteReward)

	//p.Timer().Add(1*time.Second, p.addRoll)

	p.Timer().Add(30*time.Second, p.playerSessionClose)

	//p.Timer().Add(1*time.Minute, p.upatePlayerLevelStat)

	//在线用户定时刷新
	p.Timer().Add(1*time.Minute, p.initDaily)
	p.Timer().Add(10*time.Second, p.setRedisAll)

	//p.Timer().Add(5*time.Second, p.initHourly)

	//p.Timer().AddOnce(5*time.Second, p.getAndUpdateAvatar) //排行榜头像

	//p.Timer().Add(30*time.Second, p.updPassWordTime)

	//p.Timer().Add(1*time.Minute, p.updCashOut)

	//p.Timer().Add(10*time.Second, p.invite) //检查邀请信息
}

func (p *actorPlayer) updCashOut() {
	if p.isOnline {
		go p.updCashOutStatusByOld(1)
	}
}

func (p *actorPlayer) setAvatar(req *UserAvatar) {
	if p.isOnline && p.Player != nil {
		p.Player.Avatar = req.avatar
		p.dirty = true
	}
}

func (p *actorPlayer) updPassWordTime() {
	if p.isOnline && p.Player != nil {
		if p.Player.PwdErrRefreshTime > 0 && p.Player.PwdErrRefreshTime < mhayaTime.Now().Unix() {
			p.Player.PwdErrRefreshTime = 0
			p.Player.PwdErrTimes = 0
			p.dirty = true
		}
	}
}

func (p *actorPlayer) initHourly() {
	if p.isOnline {
		s := mhayaTime.CreateFromTimestamp(p.Player.HourRefreshTime).TOHourTimeStamp()
		e := mhayaTime.Now().TOHourTimeStamp()

		if s != e {
			//刷新是否有排行榜奖励
			p.Player.RankReward.InitRank(p.Player.UserName, 1)
			p.Player.HourRefreshTime = mhayaTime.Now().Unix()
			p.dirty = true
		}
	}
}

func (p *actorPlayer) initDaily() {
	if p.isOnline && p.Player != nil {
		go func() {
			p.Player.InitDaily()
			p.dirty = true
		}()
	}
}

func (p *actorPlayer) addRoll() {
	if p.isOnline {
		if p.Player.Item[models.ItemGold].Num < 10 {
			p.Player.Item[models.ItemGold].Num += 1000
		}
		if p.Player.Item[models.ItemDrawsNumber].Num < 10 {
			p.Player.Item[models.ItemDrawsNumber].Num += 1000
		}
		p.dirty = true
	}
}

func (p *actorPlayer) upatePlayerLevelStat() {
	if p.isOnline {
		p.playerStart()
	}
}

func (p *actorPlayer) OnStop() {
	p.Timer().RemoveAll()
	clog.Debugf("[actorPlayer] path = %s exit!", p.PathString())
}

func (p *actorPlayer) setInviteReward() {
	if p.isOnline {
		p.Player.SetInviteReward()
		p.dirty = true
	}
}

func (p *actorPlayer) setRedis() {
	if p.isOnline && p.Player != nil {
		var playerBase = param.PlayerBase{
			UserName:      p.Player.UserName,
			UserNameMaybe: p.Player.UserNameMaybe,
			IsLeader:      p.Player.IsLeader,
			Avatar:        p.Player.Avatar,
			OpenId:        p.Player.OpenId,
		}
		if len(p.Player.NickName) > 0 {
			playerBase.NickName = p.Player.NickName
			d, _ := jsoniter.Marshal(&playerBase)
			mdb.RDB.HSet(context.Background(), constant.PlayerBaseKey, p.Player.UserName, string(d))
		}

	}
}

func (p *actorPlayer) SetRewardToDB() {
	go func() {
		if p.Player != nil {
			for k, r := range p.Player.PlayerReward {
				_, err := mdb.MDB.Collection(constant.CNamePlayerReward).InsertOne(context.Background(), r)
				if err != nil {
					clog.Errorf("[actorPlayer] set invite to database error: %v", err)
				}
				delete(p.Player.PlayerReward, k)
			}
		}
	}()
}

func (p *actorPlayer) setRedisAll() {
	if p.isOnline && p.Player != nil && p.dirty {
		ctx := context.Background()
		// 将玩家信息转换为 JSON 格式并存储到 Redis
		jsonData, err := jsoniter.Marshal(&p.Player)
		if err != nil {
			return
		}
		mdb.RDB.Set(ctx, fmt.Sprintf("%v:%v", constant.CNamePlayer, p.Player.OpenId), jsonData, time.Hour*3*24)
		mdb.RDB.HSet(ctx, constant.NewPlayers, p.Player.OpenId, 0)
		p.dirty = false
	}
}

func (p *actorPlayer) setDb() {
	if p.isOnline && p.dirty {
		/*		ret, err := mdb.MDB.Collection(constant.CNamePlayer).UpdateOne(context.Background(), bson.M{"openId": p.Player.OpenId}, bson.M{"$set": p.Player}, options.Update().SetUpsert(true))
				if err != nil {
					clog.Warnf("[actorPlayer] setDb ret=%v, err = %s exit!", ret, err)
				}*/

	}
}

// 获取和更新头像
func (p *actorPlayer) getAndUpdateAvatar() {
	if p.isOnline {
		go p.getAvatar()
	}
}

func (p *actorPlayer) getAvatar() {
	openid, _ := mhayaString.ToInt64(p.Player.OpenId)

	// 使用自定义 Transport 创建 HTTP 客户端
	client := &http.Client{
		Timeout: 10 * time.Second, // 设置超时时间
	}

	sdkConfig := data.SdkConfig.Get(2).Params

	url := fmt.Sprintf("https://api.telegram.org/bot%s/getUserProfilePhotos?user_id=%d", sdkConfig.BotToken, openid)

	// 发起 HTTP 请求
	req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
	if err != nil {
		clog.Warnf("Failed to create request: %v", err)
		return
	}

	resp, err := client.Do(req)
	if err != nil {
		clog.Warnf("Failed to send request: %v", err)
		return
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		clog.Warnf("Error reading body: %v", err)
		return
	}
	var result struct {
		Ok     bool `json:"ok"`
		Result struct {
			Photos [][]struct {
				FileID string `json:"file_id"`
			} `json:"photos"`
		} `json:"result"`
	}

	err = jsoniter.Unmarshal(body, &result)
	if err != nil {
		clog.Warnf("Error unmarshalling JSON: %v", err)
		return
	}

	if !result.Ok {
		clog.Warnf("Error: %s", string(body))
		return
	}
	if len(result.Result.Photos) == 0 {
		clog.Warnf("No photos found for user %d", openid)
		return
	}

	if len(result.Result.Photos) > 0 {
		// 获取第一张照片的最大尺寸
		photo := result.Result.Photos[0][len(result.Result.Photos[0])-1]

		//根据数据库记录判断是否需要更新图片库
		if photo.FileID != p.Player.Avatar {

			if p.uploadPhotoToS3(client, sdkConfig.BotToken, photo.FileID, openid) == true {
				p.Player.Avatar = photo.FileID
			}
		}
	}
}
func (p *actorPlayer) uploadPhotoToS3(client *http.Client, botToken, fileId string, openid int64) bool {

	s3 := data.SdkConfig.Get(2).S3

	url := fmt.Sprintf("https://api.telegram.org/bot%s/getFile?file_id=%s", botToken, fileId)

	// 发起 HTTP 请求
	req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
	if err != nil {
		clog.Warnf("Failed to create request: %v", err)
		return false
	}

	resp, err := client.Do(req)

	if err != nil {
		clog.Warnf("Failed to send request: %v", err)
		return false
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		clog.Warnf("Error reading body: %v", err)
		return false
	}

	var fileInfo struct {
		Ok     bool `json:"ok"`
		Result struct {
			FilePath string `json:"file_path"`
		} `json:"result"`
	}

	err = jsoniter.Unmarshal(body, &fileInfo)
	if err != nil {
		clog.Warnf("Error unmarshalling JSON: %v", err)
		return false
	}

	if !fileInfo.Ok {
		clog.Warnf("Error: %s", string(body))
		return false
	}

	downloadUrl := fmt.Sprintf("https://api.telegram.org/file/bot%s/%s", botToken, fileInfo.Result.FilePath)

	resp, err = client.Get(downloadUrl)
	if err != nil {
		clog.Warnf("Error: %v", err)
		return false
	}
	defer resp.Body.Close()

	// 配置 AWS 凭证
	sess, err := session.NewSession(&aws.Config{
		Credentials: credentials.NewStaticCredentials(
			s3.AccessKey,
			s3.SecretKey,
			"",
		),
		Region: aws.String(s3.Region), // 替换为你的 S3 区域
	})

	uploader := s3manager.NewUploader(sess)

	// 上传文件到 S3
	_, err = uploader.Upload(&s3manager.UploadInput{
		Bucket: aws.String(s3.BucketName),                        // S3 存储桶名称
		Key:    aws.String(fmt.Sprintf("avatar/%d.jpg", openid)), //当前目录存储文件
		Body:   resp.Body,
		ACL:    aws.String("private"), // 可以根据需要设置 ACL
	})

	if err != nil {
		clog.Warnf("Error uploading file: %v", err)
		return false
	}
	return true

}

func (p *actorPlayer) refreshRoll() {
	if p.isOnline {
		//if p.Player.RefreshRoll() {
		p.dirty = true
		//}
	}
}

func (p *actorPlayer) playerSessionClose() {
	deadline := time.Now().Add(-p.exitTime).Unix()
	if p.LastAt() < deadline {
		p.sessionClose()
	}
}

// sessionClose 接收角色session关闭处理
func (p *actorPlayer) sessionClose() {
	if p.Player != nil && p.isOnline {
		online.UnBindPlayer(p.uid)
		//p.setDb()
		//p.SetRewardToDB()
		p.setRedisAll()
		//logoutEvent := event.NewPlayerLogout(p.ActorID(), p.playerId, p.targetPath)
		p.isOnline = false
		p.Exit()
		//models.SetServerRecord(p.App().NodeId(), online.Count())
	}
	//p.PostEvent(&logoutEvent)
}

func (p *actorPlayer) Init(req *token.Token) bool {
	clog.Debugf("[actorPlayer] init token = %s", req)

	ctx := context.Background()
	acc, err := mdb.RDB.Get(ctx, fmt.Sprintf("%v:%v", constant.CNameAccount, req.OpenID)).Bytes()
	if err != nil && err != redis.Nil {
		clog.Debugf("[actorPlayer] initUser error: %v", err)
		return false
	}
	var account models.Account
	if len(acc) > 0 {
		err = jsoniter.Unmarshal(acc, &account)
		if err != nil {
			clog.Debugf("[actorPlayer] unmarshal account data error: %v", err)
			return false
		}
		p.Account = &account
	} else {
		dbErr := mdb.MDB.Collection(constant.CNameAccount).FindOne(context.Background(), bson.M{"openId": req.OpenID}).Decode(&account).Error()
		if dbErr != "" {
			clog.Debugf("[actorPlayer] read account data by db error: %v", err)
			return false
		}
		p.Account = &account
	}

	var player models.Player
	play, err := mdb.RDB.Get(ctx, fmt.Sprintf("%v:%v", constant.CNamePlayer, req.OpenID)).Bytes()
	if err != nil && err != redis.Nil {
		clog.Debugf("[actorPlayer] initUser error: %v", err)
		return false
	}
	if len(play) > 0 {
		err = jsoniter.Unmarshal(play, &player)
		if err != nil {
			clog.Debugf("[actorPlayer] unmarshal player data error: %v", err)
			return false
		}
		p.Player = &player
	} else {
		dbErr := mdb.MDB.Collection(constant.CNamePlayer).FindOne(context.Background(), bson.M{"userName": req.PlayerID}).Decode(&player).Error()
		if dbErr != "" {
			clog.Debugf("[actorPlayer] read player data by db error: %v", err)
			return false
		}
		p.Player = &player
	}
	if player.IsNew {
		//	go mdb.MDB.Collection(constant.CNamePlayer).InsertOne(context.Background(), user)
		p.Player.NickName = req.Nickname
		p.Player.IsNew = false
		clog.Debugf("[Init] newUser userName = %s openId = %s", p.Player.UserName, p.Player.OpenId)
		p.botInvite()
	}
	return true
}

func (p *actorPlayer) InitBase(uid string) int32 {
	clog.Debugf("[actorPlayer] InitBase uid = %s", uid)
	var player models.Player
	mdb.MDB.Collection(constant.CNamePlayer).FindOne(context.Background(), bson.M{"userName": uid}).Decode(&player)
	if player.OpenId != "" {
		p.Player = &player
		if len(p.Player.PlayerReward) == 0 {
			p.Player.PlayerReward = make(map[int]*models.PlayerRewardBase)
		}
		return code.OK
	} else {
		return code.Error
	}
}

func (p *actorPlayer) login(req *token.Token) (*models.Player, int32) {
	clog.Infof("[actorPlayer] init token = %v", req)

	if !p.isOnline {
		if !p.Init(req) {
			clog.Infof("[login] PlayerNotLogin err node=%v", p.App().NodeId())
			return nil, code.PlayerNotLogin
		}
	}
	clog.Infof("[actorPlayer] init suss token = %v", req)

	p.isOnline = true
	p.dirty = true

	p.playerId = req.PlayerID
	p.Player.UserNameMaybe = p.Account.Account
	p.Player.LoginIP = p.Account.LoginIp

	if p.Account.IsPremium {
		p.Player.IsVip = 1
	} else {
		p.Player.IsVip = 0
	}

	uid, _ := mhayaString.ToInt64(req.OpenID)
	p.uid = uid
	p.targetPath = req.TargetPath

	p.Player.Init()

	if len(req.Nickname) > 0 {
		p.Player.NickName = req.Nickname
	}

	if !mhayaTime.CreateFromTimestamp(p.Player.LoginTime).IsToday() {
		p.Player.Successions += 1
		if mhayaTime.CreateFromTimestamp(p.Player.LoginTime).DiffInDays(mhayaTime.Now()) > 1 {
			p.Player.MaxSuccessions = 1
		} else {
			p.Player.MaxSuccessions += 1
		}
	}
	p.Player.LoginTime = mhayaTime.Now().Unix()
	//刷新roll
	p.Player.InitDaily()

	//p.Player.InitWeekly()
	p.Player.InitSignIn()
	p.Player.InitAchieveTask()
	p.Player.InitInviteNodeReword()
	p.Player.InitFirstReward()

	//p.updPassWordTime()

	//p.getAndUpdateAvatar()
	//检查提现
	//go p.updCashOutStatusByOld(0)
	//统计相关
	p.loginStat()
	//更新头像
	targetPath := cfacade.NewChildPath(p.App().NodeId(), "avatar"+p.App().NodeId(), p.Player.UserName)
	p.Call(targetPath, "setAvatar", &UserAvatar{avatar: p.Player.Avatar, openId: p.Player.OpenId, userName: p.Player.UserName})

	// 保存进入游戏的玩家对应的agentPath
	online.BindPlayer(p.Player.UserName, p.uid, req.TargetPath)

	// 角色登录事件
	models.SetServerRecord(p.App().NodeId(), online.Count())
	/*	loginEvent := event.NewPlayerLogin(p.ActorID(), req)
		p.PostEvent(&loginEvent)*/

	//// todo 测试代码
	//p.Player.JoinIP = "13.229.223.106"
	return p.Player, code.OK
}
