// Copyright © 2023 OpenIM SDK. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package test

import (
	"errors"
	"fmt"
	"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
	"github.com/openimsdk/tools/log"
	"io/ioutil"
	"math/rand"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

func GetFileContentAsStringLines(filePath string) ([]string, error) {
	result := []string{}
	b, err := ioutil.ReadFile(filePath)
	if err != nil {
		return result, err
	}
	s := string(b)
	for _, lineStr := range strings.Split(s, "\n") {
		lineStr = strings.TrimSpace(lineStr)
		if lineStr == "" {
			continue
		}
		result = append(result, lineStr)
	}
	return result, nil
}

func GetCmd(myUid int, filename string) int {
	cmd, err := GetFileContentAsStringLines("cmd.txt")
	if err != nil {
		fmt.Println("GetFileContentAsStringLines failed")
		return -1
	}
	if len(cmd) < myUid {
		fmt.Println("len failed")
		return -1
	}
	return int(utils.StringToInt64(cmd[myUid-1]))
}

func ReliabilityTest(msgNumOneClient int, intervalSleepMS int, randSleepMaxSecond int, clientNum int) {
	msgNumInOneClient = msgNumOneClient
	timeStamp := utils.Int64ToString(time.Now().Unix())

	var wg sync.WaitGroup
	// 注册
	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			RegisterReliabilityUser(idx, timeStamp)
			wg.Done()
		}(i)
	}
	wg.Wait()

	log.ZWarn(ctx, "RegisterReliabilityUser finished, clientNum:", errors.New(""), "clientNum", clientNum)
	log.ZWarn(ctx, "init, login, send msg, start", errors.New(""))

	rand.Seed(time.Now().UnixNano())

	// 一半用户立刻登录发消息
	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		rdSleep := rand.Intn(randSleepMaxSecond) + 1
		isSend := 0 // 消息是否成功发送控制量
		if isSend == 0 {
			go func(idx int) {
				log.ZWarn(ctx, " send msg flag true ", errors.New(""), "idx", idx)
				ReliabilityOne(idx, rdSleep, true, intervalSleepMS)
				wg.Done()
			}(i)
			sendMsgClient++
		} else {
			go func(idx int) {
				log.ZWarn(ctx, " send msg flag false ", errors.New(""), "idx", idx)
				ReliabilityOne(idx, rdSleep, false, intervalSleepMS)
				wg.Done()
			}(i)
		}
	}
	wg.Wait()
	log.ZWarn(ctx, "send msg finish,  CheckReliabilityResult", errors.New(""))

	for {
		// 消息异步落库可能出现延迟，每隔五秒再检查一次
		if CheckReliabilityResult(msgNumOneClient, clientNum) {
			log.ZWarn(ctx, "CheckReliabilityResult ok, exit", errors.New(""))
			os.Exit(0)
			return
		} else {
			log.ZWarn(ctx, "CheckReliabilityResult failed , wait.... ", errors.New(""))
		}
		time.Sleep(time.Duration(5) * time.Second)
	}
}

func WorkGroupReliabilityTest(msgNumOneClient int, intervalSleepMS int, randSleepMaxSecond int, clientNum int, groupID string) {
	msgNumInOneClient = msgNumOneClient
	//timeStamp := utils.Int64ToString(time.Now().Unix())

	var wg sync.WaitGroup
	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			WorkGroupRegisterReliabilityUser(idx)
			wg.Done()
		}(i)
	}
	wg.Wait()

	log.ZWarn(ctx, "RegisterReliabilityUser finished", errors.New(""), "clientNum", clientNum)
	log.ZWarn(ctx, " init, login, send msg, start ", errors.New(""))
	rand.Seed(time.Now().UnixNano())

	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		rdSleep := rand.Intn(randSleepMaxSecond) + 1
		isSend := 0
		if isSend == 0 {
			go func(idx int) {
				log.ZWarn(ctx, "send msg flag true", errors.New(""), "idx", idx)
				WorkGroupReliabilityOne(idx, rdSleep, true, intervalSleepMS, groupID)
				wg.Done()
			}(i)
			sendMsgClient++
		} else {
			go func(idx int) {
				log.ZWarn(ctx, "send msg flag false", errors.New(""), "idx", idx)
				ReliabilityOne(idx, rdSleep, false, intervalSleepMS)
				wg.Done()
			}(i)
		}
	}
	wg.Wait()
	//log.Warn("send msg finish,  CheckReliabilityResult")

	for {
		if CheckReliabilityResult(msgNumOneClient, clientNum) {
			//log.Warn("", "CheckReliabilityResult ok, exit")
			os.Exit(0)
			return
		} else {
			//log.Warn("", "CheckReliabilityResult failed , wait.... ")
		}
		time.Sleep(time.Duration(5) * time.Second)
	}
}

func WorkGroupMsgDelayTest(msgNumOneClient int, intervalSleepMS int, randSleepMaxSecond int, clientBegin int, clientEnd int, groupID string) {
	msgNumInOneClient = msgNumOneClient

	var wg sync.WaitGroup

	wg.Add(clientEnd - clientBegin + 1)
	for i := clientBegin; i <= clientEnd; i++ {
		go func(idx int) {
			WorkGroupRegisterReliabilityUser(idx)
			wg.Done()
		}(i)
	}
	wg.Wait()

	//log.Warn("", "RegisterReliabilityUser finished, client: ", clientBegin, clientEnd)
	//log.Warn("", " init, login, send msg, start ")
	rand.Seed(time.Now().UnixNano())

	wg.Add(clientEnd - clientBegin + 1)
	for i := clientBegin; i <= clientEnd; i++ {
		rdSleep := rand.Intn(randSleepMaxSecond) + 1
		isSend := 0
		if isSend == 0 {
			go func(idx int) {
				//log.Warn("", " send msg flag true ", idx)
				WorkGroupReliabilityOne(idx, rdSleep, true, intervalSleepMS, groupID)
				wg.Done()
			}(i)
			sendMsgClient++
		} else {
			go func(idx int) {
				//log.Warn("", " send msg flag false ", idx)
				WorkGroupReliabilityOne(idx, rdSleep, false, intervalSleepMS, groupID)
				wg.Done()
			}(i)
		}
	}
	wg.Wait()
	//log.Warn("send msg finish,  CheckReliabilityResult")

	for {
		if CheckReliabilityResult(msgNumOneClient, clientEnd-clientBegin+1) {
			//log.Warn("", "CheckReliabilityResult ok, exit")
			os.Exit(0)
			return
		} else {
			//log.Warn("", "CheckReliabilityResult failed , wait.... ")
		}
		time.Sleep(time.Duration(5) * time.Second)
	}
}

func PressTest(msgNumOneClient int, intervalSleepMS int, clientNum int) {
	msgNumInOneClient = msgNumOneClient
	//timeStamp := utils.Int64ToString(time.Now().Unix())
	var wg sync.WaitGroup
	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			RegisterPressUser(idx)
			log.ZInfo(ctx, "GetUserTokenFinish", "index", idx)

			wg.Done()
		}(i)
	}
	wg.Wait()
	//log.Warn("", "get all user token finish ", clientNum, " cost time: ", time.Since(t1))

	//log.Warn("", "init and login begin ")

	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			strMyUid := allLoginMgr[idx].userID
			token := allLoginMgr[idx].token
			PressInitAndLogin(idx, strMyUid, token, WSADDR, APIADDR)
			wg.Done()
		}(i)
	}
	wg.Wait()
	//log.Warn("", "init and login end ", " cost time: ", time.Since(t1))

	//log.Warn("", "send msg begin ")

	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			PressOne(idx, 0, true, intervalSleepMS)
			//log.Warn("", "press finished  ", idx)
			wg.Done()
		}(i)
	}
	wg.Wait()
	sendMsgTotalSuccessNum := uint32(0)
	sendMsgTotalFailedNum := uint32(0)
	for _, v := range allLoginMgr {
		sendMsgTotalSuccessNum += v.sendMsgSuccessNum
		sendMsgTotalFailedNum += v.sendMsgFailedNum
	}
	//log.Warn("send msg end  ", "number of messages expected to be sent: ", clientNum*msgNumOneClient, " sendMsgTotalSuccessNum: ", sendMsgTotalSuccessNum, " sendMsgTotalFailedNum: ", sendMsgTotalFailedNum, "cost time: ", time.Since(t1))
}

func WorkGroupPressTest(msgNumOneClient int, intervalSleepMS int, clientNum int, groupID string) {
	msgNumInOneClient = msgNumOneClient
	var wg sync.WaitGroup
	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			WorkGroupRegisterReliabilityUser(idx)
			log.ZInfo(ctx, "GetUserTokenFinish", "index", idx)

			wg.Done()
		}(i)
	}
	wg.Wait()
	//log.Warn("", "get all user token finish ", clientNum, " cost time: ", time.Since(t1))

	//log.Warn("", "init and login begin ")

	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			strMyUid := allLoginMgr[idx].userID
			token := allLoginMgr[idx].token
			ReliabilityInitAndLogin(idx, strMyUid, token, WSADDR, APIADDR)
			wg.Done()
		}(i)
	}
	wg.Wait()
	//log.Warn("", "init and login end ", " cost time: ", time.Since(t1))

	//log.Warn("", "send msg begin ")

	wg.Add(clientNum)
	for i := 0; i < clientNum; i++ {
		go func(idx int) {
			WorkGroupPressOne(idx, 0, true, intervalSleepMS, groupID)
			wg.Done()
		}(i)
	}
	wg.Wait()
	sendMsgTotalSuccessNum := uint32(0)
	sendMsgTotalFailedNum := uint32(0)
	for _, v := range allLoginMgr {
		sendMsgTotalSuccessNum += v.sendMsgSuccessNum
		sendMsgTotalFailedNum += v.sendMsgFailedNum
	}
	//log.Warn("send msg end  ", "number of messages expected to be sent: ", clientNum*msgNumOneClient, " sendMsgTotalSuccessNum: ", sendMsgTotalSuccessNum, " sendMsgTotalFailedNum: ", sendMsgTotalFailedNum, "cost time: ", time.Since(t1))
}

func CheckReliabilityResult(msgNumOneClient int, clientNum int) bool {
	log.ZInfo(ctx, "StartCheckMapSendToMapRecv")

	sameNum := 0

	// 消息数量不一致说明出现丢失
	if len(SendSuccAllMsg)+len(SendFailedAllMsg) != msgNumOneClient*clientNum {
		//log.Warn("", utils.GetSelfFuncName(), " send msg success number: ", len(SendSuccAllMsg),
		//	" send msg failed number: ", len(SendFailedAllMsg), " all: ", msgNumOneClient*clientNum)
		return false
	}

	for ksend, _ := range SendSuccAllMsg {
		_, ok := RecvAllMsg[ksend] // RecvAllMsg 的初始化何时？
		if ok {
			sameNum++
		} else {
			// 埋点日志，第 ksend 个消息数据 本地和服务器不一致
			//log.Error("", "check failed not in recv ", ksend)
			//log.Error("", "send failed num: ", len(SendFailedAllMsg),
			//	" send success num: ", len(SendSuccAllMsg), " recv num: ", len(RecvAllMsg))
			return false
		}
	}
	log.ZInfo(ctx, "CheckMapSendToMapRecvOK", "sameNum", sameNum)

	//log.Info("", "start check map recv -> map send ")
	//sameNum = 0

	//for k1, _ := range RecvAllMsg {
	//	_, ok := SendSuccAllMsg[k1]
	//	if ok {
	//		sameNum++
	//		//x := v1 + v2
	//		//x = x + x
	//
	//	} else {
	//		log.Error("", "check failed  not in send ", k1, len(SendFailedAllMsg), len(SendSuccAllMsg), len(RecvAllMsg))
	//		//	return false
	//	}
	//}
	minCostTime := int64(1000000)
	maxCostTime := int64(0)
	totalCostTime := int64(0)
	for ksend, vsend := range SendSuccAllMsg {
		krecv, ok := RecvAllMsg[ksend]
		if ok {
			sameNum++
			costTime := krecv.RecvTime - vsend.SendTime
			totalCostTime += costTime
			if costTime > maxCostTime {

				maxCostTime = costTime
			}
			if minCostTime > costTime {
				minCostTime = costTime
			}
		}
	}

	//log.Warn("", "need send msg num : ", sendMsgClient*msgNumInOneClient)
	//log.Warn("", "send msg succ num ", len(SendSuccAllMsg))
	//log.Warn("", "send msg failed num ", len(SendFailedAllMsg))
	//log.Warn("", "recv msg succ num ", len(RecvAllMsg))
	//log.Warn("", "minCostTime: ", minCostTime, "ms, maxCostTime: ", maxCostTime, "ms, average cost time: ",
	//	totalCostTime/(int64(sendMsgClient*msgNumInOneClient)), "ms", " maxCostMsgID: ", maxCostMsgID)

	return true
}

func ReliabilityOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int) {
	//	time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
	strMyUid := allLoginMgr[index].userID
	token := allLoginMgr[index].token
	log.ZInfo(ctx, "LoginOKClientNum", "clientNum", len(allLoginMgr), "userID", strMyUid, "token", token, "index", index)

	ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)

	//log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)

	msgnum := msgNumInOneClient
	uidNum := len(allLoginMgr)
	rand.Seed(time.Now().UnixNano())
	if msgnum == 0 {
		os.Exit(0)
	}
	if !isSendMsg {
		//	Msgwg.Done()
	} else {
		for i := 0; i < msgnum; i++ {
			var r int
			time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
			for {
				r = rand.Intn(uidNum)
				if r == index {
					continue
				} else {
					break
				}
			}
			recvId := allLoginMgr[r].userID
			idx := strconv.FormatInt(int64(i), 10)
			for {
				if runtime.NumGoroutine() > MaxNumGoroutine {
					time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
					//log.Warn("", "NumGoroutine > max  ", runtime.NumGoroutine(), MaxNumGoroutine)
					continue
				} else {
					break
				}
			}
			DoTestSendMsg(index, strMyUid, recvId, "", idx)
		}
		//Msgwg.Done()
	}
}

func WorkGroupReliabilityOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
	//	time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
	strMyUid := allLoginMgr[index].userID
	token := allLoginMgr[index].token
	ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
	log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))

	//log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)
	msgnum := msgNumInOneClient
	uidNum := len(allLoginMgr)
	var idx string
	rand.Seed(time.Now().UnixNano())
	if msgnum == 0 {
		os.Exit(0)
	}
	if !isSendMsg {
		//	Msgwg.Done()
	} else {
		for i := 0; i < msgnum; i++ {
			var r int
			time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
			for {
				r = rand.Intn(uidNum)
				if r == index {
					continue
				} else {

					break
				}

			}

			idx = strconv.FormatInt(int64(i), 10)
			for {
				if runtime.NumGoroutine() > MaxNumGoroutine {
					time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
					//log.Warn("", "NumGoroutine > max  ", runtime.NumGoroutine(), MaxNumGoroutine)
					continue
				} else {
					break
				}
			}

			DoTestSendMsg(index, strMyUid, "", groupID, idx)

		}
		//Msgwg.Done()
	}
}

func WorkGroupMsgDelayOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
	//	time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
	strMyUid := allLoginMgr[index].userID
	token := allLoginMgr[index].token
	ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
	log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))

	//log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)
	msgnum := msgNumInOneClient
	uidNum := len(allLoginMgr)
	var idx string
	rand.Seed(time.Now().UnixNano())
	if msgnum == 0 {
		os.Exit(0)
	}
	if !isSendMsg {
		//	Msgwg.Done()
	} else {
		for i := 0; i < msgnum; i++ {
			var r int
			time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
			for {
				r = rand.Intn(uidNum)
				if r == index {
					continue
				} else {

					break
				}

			}

			idx = strconv.FormatInt(int64(i), 10)
			for {
				if runtime.NumGoroutine() > MaxNumGoroutine {
					time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
					//log.Warn("", "NumGoroutine > max  ", runtime.NumGoroutine(), MaxNumGoroutine)
					continue
				} else {
					break
				}
			}

			DoTestSendMsg(index, strMyUid, "", groupID, idx)

		}
		//Msgwg.Done()
	}
}

//
//funcation WorkGroupMsgDelayOne(sendID1 string, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
//	//	time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
//	strMyUid := allLoginMgr[index].userID
//	token := allLoginMgr[index].token
//	ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
//	log.Info("", "login ok client num: ", len(allLoginMgr))
//	log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)
//	msgnum := msgNumInOneClient
//	uidNum := len(allLoginMgr)
//	var idx string
//	rand.Seed(time.Now().UnixNano())
//	if msgnum == 0 {
//		os.Exit(0)
//	}
//	if !isSendMsg {
//		//	Msgwg.Done()
//	} else {
//		for i := 0; i < msgnum; i++ {
//			var r int
//			time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
//			for {
//				r = rand.Intn(uidNum)
//				if r == index {
//					continue
//				} else {
//
//					break
//				}
//
//			}
//
//			idx = strconv.FormatInt(int64(i), 10)
//			for {
//				if runtime.NumGoroutine() > MaxNumGoroutine {
//					time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
//					log.Warn("", "NumGoroutine > max  ", runtime.NumGoroutine(), MaxNumGoroutine)
//					continue
//				} else {
//					break
//				}
//			}
//
//			DoTestSendMsg(index, strMyUid, "", groupID, idx)
//
//		}
//		//Msgwg.Done()
//	}
//}

func PressOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int) {
	if beforeLoginSleep != 0 {
		time.Sleep(time.Duration(beforeLoginSleep) * time.Millisecond)
	}

	strMyUid := allLoginMgr[index].userID
	token := allLoginMgr[index].token
	//	ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
	log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))
	log.ZInfo(ctx, "StartOne", "index", index, "beforeLoginSleep", beforeLoginSleep, "isSendMsg", isSendMsg, "senderUID", strMyUid, "token", token, "wsAddr", WSADDR, "apiAddr", APIADDR)

	msgnum := msgNumInOneClient
	uidNum := len(allLoginMgr)
	var recvId string
	var idx string
	rand.Seed(time.Now().UnixNano())
	if msgnum == 0 {
		os.Exit(0)
	}
	if !isSendMsg {
		//	Msgwg.Done()
	} else {
		for i := 0; i < msgnum; i++ {
			var r int
			//	time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
			for {
				r = rand.Intn(uidNum)
				if r == index {
					continue
				} else {

					break
				}

			}

			recvId = allLoginMgr[r].userID
			idx = strconv.FormatInt(int64(i), 10)
			for {
				if runtime.NumGoroutine() > MaxNumGoroutine {
					time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
					//log.Warn("", " NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
					continue
				} else {
					break
				}
			}
			time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
			//DoTestSendMsg(index, strMyUid, recvId, idx)
			if sendPressMsg(index, strMyUid, recvId, "", idx) {
				allLoginMgr[index].sendMsgSuccessNum++
			} else {
				allLoginMgr[index].sendMsgFailedNum++
			}
		}
		//Msgwg.Done()
	}
}

func WorkGroupPressOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
	if beforeLoginSleep != 0 {
		time.Sleep(time.Duration(beforeLoginSleep) * time.Millisecond)
	}
	strMyUid := allLoginMgr[index].userID
	token := allLoginMgr[index].token
	//ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
	log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))
	log.ZInfo(ctx, "StartOne", "index", index, "beforeLoginSleep", beforeLoginSleep, "isSendMsg", isSendMsg, "senderUID", strMyUid, "token", token, "wsAddr", WSADDR, "apiAddr", APIADDR)

	msgnum := msgNumInOneClient
	var idx string
	rand.Seed(time.Now().UnixNano())
	if msgnum == 0 {
		os.Exit(0)
	}
	if !isSendMsg {
	} else {
		for i := 0; i < msgnum; i++ {
			idx = strconv.FormatInt(int64(i), 10)

			for {
				if runtime.NumGoroutine() > MaxNumGoroutine {
					time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
					//log.Warn("", " NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
					continue
				} else {
					break
				}
			}
			log.ZInfo(ctx, "SendPressMsgBegin", "index", index, "senderUID", strMyUid, "groupID", groupID)

			if sendPressMsg(index, strMyUid, "", groupID, idx) {
				allLoginMgr[index].sendMsgSuccessNum++
			} else {
				allLoginMgr[index].sendMsgFailedNum++
			}
			log.ZInfo(ctx, "sendPressMsg end")
			time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
		}
	}
}
