package test

import (
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sws_xcx/enums"
	"sws_xcx/models"
	_ "sws_xcx/routers"
	"sws_xcx/service"
	"sws_xcx/utils"
	"testing"
	"time"
	"unicode/utf8"

	//beego "github.com/beego/beego/v2/server/web"
	//"github.com/beego/beego/v2/core/logs"
	"github.com/astaxie/beego"
	"github.com/jinzhu/gorm"
	"github.com/medivhzhan/weapp/v3/auth"
	// "github.com/smartystreets/goconvey/convey"
)

func init() {
	_, file, _, _ := runtime.Caller(0)
	apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".."+string(filepath.Separator))))
	beego.TestBeegoInit(apppath)
}

// TestGet is a sample to run an endpoint test
func TestGet(t *testing.T) {
	// r, _ := http.NewRequest("GET", "/v1/object", nil)
	// w := httptest.NewRecorder()
	// beego.BeeApp.Handlers.ServeHTTP(w, r)

	// logs.Info("testing", "TestGet", "Code[%d]\n%s", w.Code, w.Body.String())

	// Convey("Subject: Test Station Endpoint\n", t, func() {
	//         Convey("Status Code Should Be 200", func() {
	//                 So(w.Code, ShouldEqual, 200)
	//         })
	//         Convey("The Result Should Not Be Empty", func() {
	//                 So(w.Body.Len(), ShouldBeGreaterThan, 0)
	//         })
	// })
	t.Log("TestGet")
}

func TestWxappSessionCode(t *testing.T) {

	sdk := service.GetWxSdk()
	wxcli := sdk.NewAuth()

	code := "0d3eVp100qtUES1jgD300t5yN21eVp1t"

	resp, err := wxcli.Code2Session(&auth.Code2SessionRequest{JsCode: code, GrantType: "authorization_code", Appid: beego.AppConfig.String("appid"), Secret: beego.AppConfig.String("appsecret")})
	if err != nil {
		t.Error(err)
		return
	}
	if resp.CommonError.ErrCode != 0 {
		t.Errorf("err code:%v msg:%v", resp.CommonError.ErrCode, resp.CommonError.ErrMSG)
		return
	}
	t.Logf("resp: %+v", *resp)

}
func TestJson(t *testing.T) {
	req := `{"id":1,"nick_name":"zhangsan","ctime":"2019-11-20 16:49:08"}`
	u := models.UserInfoResp{}
	err := json.Unmarshal([]byte(req), &u)
	if err != nil {
		t.Error(err)
	}
	t.Logf("u:%+v", u)

}

func TestHandleMsg(t *testing.T) {

	req := []byte(`{"publish_received_at":1722839054218,"pub_props":{"User-Property":{}},"peerhost":"124.133.43.122","qos":0,"topic":"/CH-U100/09012201100010104/user/checkresult","clientid":"42915152F3F1","payload":"{\"productKey\":\"CH-U100\",\"deviceName\":\"09012201100010104\",\"acc\":12378,\"type\":0,\"count\":14,\"datatype\":0,\"verifycode\":212,\"data\":\"0,0,0,2,2,0,0,0,0,0,4,3,0,1\",\"origin\":\"4243305A0000010E0000000202000000000004030001D4\",\"len\":23}","username":"09012201100010104","event":"message.publish","metadata":{"rule_id":"checkresult"},"timestamp":1722839054218,"node":"emqx@127.0.0.1","id":"00061EE9B9D4F6AFF445000924860003","flags":{"retain":false,"dup":false}}`)

	msg := &models.OpenEmqMsgReqVO{}
	err := json.Unmarshal(req, msg)
	if err != nil {
		t.Error(err)
		return
	}

	ds := service.NewDeviceService()

	device, err := ds.GetDeviceByNo(msg.UserName)
	if err != nil && err != gorm.ErrRecordNotFound {
		utils.ErrorLog("GetDeviceByNo %v:", err)
		t.Error(err)
		return
	}
	if device.Id == 0 {
		utils.WarningLog("非本系统设备，数据废弃")
		return
	}
	err = ds.CreateDeviceMessageLog(models.DeviceMessageLog{
		DeviceName: msg.DeviceName,
		Content:    string(req),
		EventType:  enums.EventTypeEmqx,
		MessageId:  msg.Id,
		Topic:      msg.Topic,
	})

	if err != nil {
		utils.ErrorLog("CreateDeviceMessageLog %v:", err)
		t.Error(err)
		return
	}
	payload := &models.OpenEmqPayloadReqVO{}
	err = json.Unmarshal([]byte(msg.Payload), payload)
	if err != nil {
		utils.ErrorLog("json.Unmarshal %v:", err)
		t.Error(err)
		return
	}
	if payload.Len != 23 {
		utils.WarningLog("非标准数据，数据废弃:%v", msg.Payload)
	}

	dr, err := ds.GetDeviceRelateByDeviceId(device.Id)
	if err != nil {
		utils.ErrorLog("GetDeviceRelateByDeviceId %v:", err)
		t.Error(err)
		return
	}

	checkRecord := &models.CheckRecord{

		Acc:          payload.Acc,
		PutSources:   enums.EventTypeEmqx,
		MessageId:    msg.Id,
		DeviceId:     device.Id,
		UserId:       dr.UserId,
		DeviceStatus: device.Status,
	}

	if dr.UserId > 0 {

		hp, err := service.NewUserHealthProfileService().GetUserHealthProfileByUserId(dr.UserId)
		if err != nil {
			utils.ErrorLog("GetUserHealthProfileByUserId %v:", err)
			t.Error(err)
			return
		}
		checkRecord.UserHealthProfileId = int64(hp.Id)
	}
	crs := service.NewCheckRecordService()
	err = crs.CreateCheckRecord(checkRecord)
	if err != nil {
		utils.ErrorLog("CreateCheckRecord %v:", err)
		t.Error(err)
		return
	}
	t.Log(checkRecord.Id)
	t.Log(payload.Data)

	da := strings.Split(payload.Data, ",")
	count := payload.Count
	if len(da) != count {
		t.Logf("数据长度不匹配,截取存储 %+v", payload)
		if len(da) < count {
			count = len(da)
		}
	}

	checkItems, err := service.NewCheckItemService().GetCheckItems("cn", device.DeviceType)

	if err != nil {
		utils.ErrorLog("GetCheckItems %v:", err)
		t.Error(err)
		return
	}

	if len(checkItems) < count {
		t.Error("接收的数据于检查项目配置不一致")
		return
	}

	alerts := make([]string, 0)
	for i := 0; i < count; i++ {
		vi, _ := strconv.Atoi(da[i])
		err = createCheckRecordItem(checkItems, checkRecord.Id, &alerts, vi, i+1, crs)
		if err != nil {
			utils.ErrorLog("createCheckRecordItem valule:%v, err:%v:", vi, err)
		}
	}

	if len(alerts) > 0 {
		alertItemIds := strings.Join(alerts, ",")
		err = crs.UpdateCheckRecordAlertItems(checkRecord.Id, alertItemIds)
		if err != nil {
			utils.ErrorLog("UpdateCheckRecordAlertItems %v:", err)
			t.Error(err)
			return
		}

	} else {
		utils.TraceLog("无异常数据")
	}

}

var DIGITS = []rune{
	'S', 'V', 'X', '5', '0', '8', '-', 'N',
	'i', 'm', 'd', 'u', 'H', 'O', 'Q', 'E',
	'D', 'v', 't', '2', 'c', 'p', '1', '3',
	'z', 'R', 'a', 'W', 'l', 'G', 'k', 'A',
	'9', 'o', 's', 'g', 'y', '4', 'w', 'L',
	'T', 'Y', 'b', '+', 'J', 'x', '7', 'r',
	'I', 'n', 'f', 'Z', 'e', 'M', 'C', 'P',
	'U', 'B', 'j', '6', 'F', 'K', 'h', 'q',
}

func TestHandleQrCode(t *testing.T) {

	result := "http://d.j6c.cn/E0YUQ|XGU|5Vy.z6k&EZf"
	//result = "E0YUQ|XGU|5Vy.z6k&EZf"
	var userId uint64

	if result != "" {

		re := regexp.MustCompile(`(http.*/)?(.+)$`)
		matches := re.FindStringSubmatch(result)

		str := matches[2]

		parity := str[len(str)-1:]
		st := int8(0)
		result1 := result[:len(result)-1]

		for i := 0; i < utf8.RuneCountInString(result1); i++ {
			r, _ := utf8.DecodeRuneInString(result1[i:])
			st += int8(r)
			st = int8(getUint8(int(st)))
		}
		digit := DIGITS[getUint8(int(^st))%64]
		if string(digit) != parity {
			t.Error("检测数据校验不通过")
			return
		}
		item, err := strconv.ParseInt(str[:1], 16, 32)
		if err != nil {
			t.Error(err)
			return
		}
		if item != 14 && item != 11 {
			t.Error("无法解析检测结果")
			return
		}

		strs := str[1 : len(str)-1]
		strs = regexp.MustCompile(`[._&$|]`).ReplaceAllString(strs, " ")
		strsArray := strings.Fields(strs)

		if len(strsArray) < 5 {
			t.Error("无法解析检测结果")
			return
		}

		serialno := "09" + formatNumber(radixString(strsArray[0]), 8)
		serialno += formatNumber(radixString(strsArray[1]), 7)

		acc := int(radixString(strsArray[2]))

		checkResultStr := formatNumber(radixString(strsArray[3]), 7)
		if item == 14 {
			checkResultStr += formatNumber(radixString(strsArray[4]), 7)
		} else if item == 11 {
			checkResultStr += formatNumber(radixString(strsArray[4]), 4)
		}

		fmt.Println("Device Name:", serialno)
		fmt.Println("Acc:", acc)
		fmt.Println("Check Result String:", checkResultStr)

		device, err := service.NewDeviceService().GetDeviceByNo(serialno)
		if err != nil {
			t.Error(err)
			return
		}
		if device.Id == 0 {
			t.Error("不是本系统中的设备，无法解析结果")
			return
		}
		ds := service.NewDeviceService()
		dr, err := ds.GetDeviceRelateByDeviceId(device.Id)
		if err != nil {
			t.Error(err)
			return
		}
		var status int
		if dr.Id == 0 {
			//设备未绑定 提示绑定
			status = 1

		} else if dr.UserId != userId {
			//别人已经绑定设备，返回结果 并提示
			status = 2

		} else {
			//自己绑定的设备，返回结果不提示
			status = 3
		}
		da := strings.Split(checkResultStr, "")
		count := len(da)
		checkItems, err := service.NewCheckItemService().GetCheckItems("cn", device.DeviceType)

		if err != nil {
			utils.ErrorLog("GetCheckItems %v:", err)
			t.Error(err)
			return
		}
		crs := service.NewCheckRecordService()
		record, err := crs.GetCheckRecordByAcc(device.Id, acc)
		if err != nil {
			utils.ErrorLog("GetCheckRecordByAcc %v:", err)
			t.Error(err)
		}
		if record.Id == 0 {
			//创建记录
			err = ds.CreateDeviceMessageLog(models.DeviceMessageLog{
				DeviceName: device.DeviceName,
				Content:    result,
				EventType:  enums.EventTypeQrCode,
			})
			if err != nil {
				utils.ErrorLog("CreateDeviceMessageLog %v:", err)
				t.Error(err)
				return
			}
			if len(checkItems) < count {
				t.Error("接收的数据与检查项目配置不一致")
				return
			}
			checkRecord := &models.CheckRecord{
				Acc:          acc,
				PutSources:   enums.EventTypeQrCode,
				DeviceId:     device.Id,
				UserId:       dr.UserId,
				DeviceStatus: device.Status,
				Ctime:        time.Now(),
			}
			if dr.UserId > 0 {

				hp, err := service.NewUserHealthProfileService().GetUserHealthProfileByUserId(dr.UserId)
				if err != nil {
					utils.ErrorLog("GetUserHealthProfileByUserId %v:", err)
					t.Error(err)
					return
				}
				checkRecord.UserHealthProfileId = int64(hp.Id)
			}
			err = crs.CreateCheckRecord(checkRecord)
			if err != nil {
				utils.ErrorLog("CreateCheckRecord %v:", err)
				t.Error(err)
				return
			}
			alerts := make([]string, 0)
			for i := 0; i < count; i++ {
				vi, _ := strconv.Atoi(da[i])
				err = createCheckRecordItem(checkItems, checkRecord.Id, &alerts, vi, i+1, crs)
				if err != nil {
					utils.ErrorLog("createCheckRecordItem valule:%v, err:%v:", vi, err)
				}
			}

			if len(alerts) > 0 {
				alertItemIds := strings.Join(alerts, ",")
				err = crs.UpdateCheckRecordAlertItems(checkRecord.Id, alertItemIds)
				if err != nil {
					utils.ErrorLog("UpdateCheckRecordAlertItems %v:", err)
					t.Error(err)
					return
				}

			} else {
				utils.TraceLog("无异常数据")
			}
			record = *checkRecord
		}

		recordItems, err := crs.GetCheckRecordItems(record.Id)
		if err != nil {
			utils.ErrorLog("GetCheckRecordItems %v:", err)
			t.Error(err)
			return
		}
		checkRecordItem, err := analyzeCheckRecord(recordItems, checkItems)
		if err != nil {
			t.Error(err)
			return
		}
		checkRecordItem.Status = status
		checkRecordItem.CheckDate = models.Time(record.Ctime)
		checkRecordItem.UserID = int64(record.UserId)
		checkRecordItem.Bind = checkRecordItem.UserID > 0
		checkRecordItem.CheckRecordID = record.Id

		t.Logf("result:%+v", checkRecordItem)

	}

}

func analyzeCheckRecord(ritems []*models.CheckRecordItem, citems []*models.CheckItem) (models.AppCheckRecordItemVO, error) {
	var result models.AppCheckRecordItemVO
	var details []models.AppCheckRecordItemDetailsVO

	for _, ri := range ritems {
		detail := models.AppCheckRecordItemDetailsVO{}
		item := findItem(citems, ri.CheckItemId)
		if item == nil {
			continue
		}
		if ri.CheckValue != "" && item.ScopeList != "" {
			var scopes []*models.CheckItemScopeVO
			err := json.Unmarshal([]byte(item.ScopeList), &scopes)
			if err != nil {
				return result, err
			}
			for _, scope := range scopes {
				if ri.CheckValueIndex == scope.Index {
					if scope.Type == 1 {
						result.NormalCount += 1
						detail.Alert = false

					} else {
						result.AlertCount += 1
						detail.Alert = true
					}
					detail.ValueIndex = strconv.Itoa(ri.CheckValueIndex)
					detail.ID = item.Id
					detail.Value = scope.Value
					detail.NameCn = item.NameCn
					detail.NameEn = item.NameEn
					detail.ReferenceValue = item.ReferenceValue
					//detail.Describe = item.Details
					detail.Unit = item.Unit
					details = append(details, detail)
					break
				}
			}
			result.Items = details
			if result.AlertCount > 1 {
				result.AlertGrade = enums.AlertGradeHIGH
			} else if result.AlertCount == 1 {
				result.AlertGrade = enums.AlertGradeMEDIUM

			} else {
				result.AlertGrade = enums.AlertGradeNORMAL
			}
			result.Describe = enums.AlertGradeMap[result.AlertGrade]
		}
	}

	return result, nil
}

func formatNumber(num int64, width int) string {
	format := fmt.Sprintf("%%0%dd", width)
	return fmt.Sprintf(format, num)
}

func getUint8(val int) uint8 {
	return uint8(val & 0xFF)
}

func indexDigits(ch rune) int {
	for i, digit := range DIGITS {
		if ch == digit {
			return i
		}
	}
	return -1
}

func radixString(str string) int64 {
	sum := int64(0)
	len := len(str)

	for i := 0; i < len; i++ {
		index := indexDigits(rune(str[len-i-1]))
		sum += int64(index) * int64(math.Pow(64, float64(i)))
	}

	return sum
}

func createCheckRecordItem(items []*models.CheckItem, crId int64, alerts *[]string, vi, n int, crs *service.CheckRecordService) error {
	item := findItem(items, n)
	if item == nil {
		return fmt.Errorf("检测项目不匹配，需要检查基础数据！序号：%v", n)
	}

	cri := models.CheckRecordItem{
		CheckId:         crId,
		CheckItemId:     n,
		CheckValueIndex: vi,
	}

	scopes := []*models.CheckItemScopeVO{}
	err := json.Unmarshal([]byte(item.ScopeList), &scopes)
	if err != nil {
		return fmt.Errorf("检测项目数值范围定义不正确 err:%v", err)
	}
	scope := findScope(scopes, vi)
	if scope == nil {
		return errors.New("检测结果不匹配")
	}

	cri.CheckValue = scope.Value
	if scope.Type == 1 {
		*alerts = append(*alerts, strconv.Itoa(n))
	}
	return crs.CreateCheckRecordItem(&cri)
}

func findScope(scopes []*models.CheckItemScopeVO, vi int) *models.CheckItemScopeVO {
	for _, scope := range scopes {
		if scope.Index == vi {
			return scope
		}
	}
	return nil
}

func findItem(items []*models.CheckItem, itemNumber int) *models.CheckItem {
	//循环items
	for _, item := range items {
		if item.CheckItemNumber == itemNumber {
			return item
		}
	}
	return nil
}
