package app

import (
	"context"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	waProto "git.bobomao.top/joey/testwh/binary/proto"
	"github.com/gogf/gf/util/gconv"
	"github.com/golang/protobuf/proto"
	"github.com/ulule/deepcopier"
	"log"
)

// IsEmpty
func IsEmpty(s string) bool {
	return s == "" || len(s) <= 0
}

type AuthDataDto struct {
	*ClientPayload
}

type AuthDataDtoActual struct {
	*waProto.ClientPayload
}

// LoginDto 登录信息
type LoginDto struct {
	// socks 5 代理
	UUID           string
	Socks5         string
	AuthBody       *AuthDataDto
	AuthBodyActual *AuthDataDtoActual
	// AuthHexData 握手认证数据
	AuthHexData string
	// StaticPriKey 认证私钥
	StaticPriKey string
	// StaticPubKey 认证公钥
	StaticPubKey string
	//注册返回
	ClientStaticKeypair string
	//EdgeRouting
	EdgeRouting string
	//
	IdentityPubKey string
	IdentityPriKey string
	Hash           string
}

func (loginDto *LoginDto) Init() {
	loginDto.AuthBodyActual = &AuthDataDtoActual{
		ClientPayload: &waProto.ClientPayload{
			UserAgent: &waProto.ClientPayload_UserAgent{
				AppVersion: &waProto.ClientPayload_UserAgent_AppVersion{},
			},
		},
	}
	//playload := loginDto.AuthBodyActual.ClientPayload
	//playloadActual := loginDto.AuthBodyActual.ClientPayload
	playload := loginDto.AuthBody.ClientPayload
	playloadActual := loginDto.AuthBodyActual.ClientPayload
	deepcopier.Copy(playload).To(playloadActual)
	deepcopier.Copy(playload.UserAgent).To(playloadActual.UserAgent)
	deepcopier.Copy(playload.UserAgent.AppVersion).To(playloadActual.UserAgent.AppVersion)
	playloadActual.UserAgent.Platform = (*waProto.ClientPayload_UserAgent_Platform)(playload.UserAgent.Platform)

	//playload

}

type DHKey struct {
	Private []byte
	Public  []byte
}

var DHString = []byte{69, 68, 0, 1}
var InitString = []byte("WA")

type VerifyCallbackFunc func(publicKey []byte, data []byte) error

// WAProtocolVersion noise 握手版本
type WAProtocolVersion struct {
	// 主要版本
	VersionMajor byte
	// 次要版本
	VersionMinor byte
}

type WAHandshakeSettings struct {
	RoutingInfo []byte
	//认证数据
	Payload []byte //certificates, signs etc
	// 用户注册时生成的公私秘钥
	StaticKey DHKey
	// 服务器公钥
	PeerStatic []byte
}

// loginInfo
type loginInfo struct {
	ctx context.Context
	// login info
	clientPayload  *waProto.ClientPayload
	priKey, pubKey []byte
	routingInfo    []byte
	staticPubKey   *[32]byte
	staticPriKey   *[32]byte
}

func (l *loginInfo) GetStaticPubKey() *[32]byte {
	return l.staticPubKey
}

func (l *loginInfo) SetStaticPubKey(d string) error {
	data, err := base64.StdEncoding.DecodeString(d)
	if err != nil {
		return err
	}
	if len(data) == 33 {
		data = data[1:]
	}
	var byteArray [32]byte
	copy(byteArray[:], data)
	l.staticPubKey = &byteArray
	return nil
}
func (l *loginInfo) GetStaticPriKey() *[32]byte {
	return l.staticPriKey
}
func (l *loginInfo) SetStaticPriKey(d string) error {
	data, err := base64.StdEncoding.DecodeString(d)
	if err != nil {
		return err
	}
	if len(data) == 33 {
		data = data[1:]
	}
	var byteArray [32]byte
	copy(byteArray[:], data)
	l.staticPriKey = &byteArray
	return nil
}

func (l *loginInfo) Ctx() context.Context {
	return l.ctx
}

func (l *loginInfo) SetRoutingInfo(d []byte) {
	l.routingInfo = d
}

func (l *loginInfo) SetStaticHdBase64Keys(pri, pub string) error {
	priData, err := base64.StdEncoding.DecodeString(pri)
	if err != nil {
		return err
	}
	pubData, err := base64.StdEncoding.DecodeString(pub)
	if err != nil {
		return err
	}

	if len(priData) != 32 || len(pubData) < 32 {
		return errors.New("keys length less than 32 bit")
	}

	if len(pubData) == 33 {
		pubData = pubData[1:]
	}
	if len(priData) == 33 {
		priData = priData[1:]
	}
	// set keys
	l.priKey, l.pubKey = priData, pubData
	return nil
}

func (l *loginInfo) SetStaticHdKeys(pri, pub []byte) error {
	if len(pri) != 32 || len(pub) != 32 {
		return errors.New("keys length less than 32 bit")
	}
	// set keys
	l.priKey, l.pubKey = pri, pub
	return nil
}

// SetStaticKeys
func (l *loginInfo) SetStaticKeys(base64Data string) error {
	skData, err := base64.StdEncoding.DecodeString(base64Data)
	if err != nil {
		return err
	}
	// if sk data length not 64
	if len(skData) < 64 {
		return errors.New("static keys length less than 64 bit")
	}
	// set static keys
	l.priKey, l.pubKey = skData[:32], skData[32:]
	return nil
}

func (l *loginInfo) SetCliPayload(payload *waProto.ClientPayload) {
	if payload != nil {
		l.clientPayload = payload
	}
}

// SetCliPayloadData
func (l *loginInfo) SetCliPayloadData(data []byte) error {
	return proto.Unmarshal(data, l.clientPayload)
}

// GetLoginSettings
func (l *loginInfo) GetLoginSettings() *WAHandshakeSettings {
	return &WAHandshakeSettings{
		RoutingInfo: l.routingInfo,
		Payload:     l.buildClientPayload(),
		StaticKey: DHKey{
			Private: l.priKey,
			Public:  l.pubKey,
		},
		PeerStatic: []byte{},
	}
}

func (l *loginInfo) buildClientPayload() []byte {
	if l.clientPayload == nil {
		return nil
	}
	d, _ := json.MarshalIndent(&l.clientPayload, " ", "  ")
	log.Println("AUTHDATA:", string(d))
	//db.PushQueue(
	//	db.PushMsg{
	//		Time:     time.Now().Unix(),
	//		UserName: l.clientPayload.GetUsername(),
	//		Type:     db.System.Number(),
	//		Data:     l.clientPayload,
	//	},
	//)
	// marshal
	payloadData, err := proto.Marshal(l.clientPayload)
	if err != nil {
		return nil
	}
	log.Println("payloadData", hex.EncodeToString(payloadData))
	return payloadData
}

// AccountInfo
type AccountInfo struct {
	*loginInfo
	verifiedName uint64
}

func EmptyAccountInfo() *AccountInfo {
	return &AccountInfo{
		loginInfo: &loginInfo{
			ctx:           context.Background(),
			clientPayload: &waProto.ClientPayload{},
			priKey:        []byte{},
			pubKey:        []byte{},
			routingInfo:   []byte{},
		}}
}

// GetUserName
func (a *AccountInfo) GetUserName() string {
	if a.clientPayload != nil {
		return gconv.String(a.clientPayload.GetUsername())
	}
	return ""
}
func (a *AccountInfo) GetVeriFiledName() uint64 {
	if a.verifiedName != 0 {
		return a.verifiedName
	}
	return 0
}
func (a *AccountInfo) SetVeriFiledName(VeriFiledName uint64) {
	a.verifiedName = VeriFiledName
}

// 获取平台
func (a *AccountInfo) GetPlatform() string {
	if a.clientPayload != nil {
		return a.clientPayload.GetUserAgent().GetPlatform().String()
	}
	return "no"
}

// SetLogCtx
func (a *AccountInfo) SetLogCtx(k, v string) {
	a.ctx = context.WithValue(a.ctx, k, v)
}

func (a *AccountInfo) SetUserName(u uint64) {
	if a.clientPayload != nil {
		a.clientPayload.Username = proto.Uint64(u)
		//a.clientPayload.SessionId = proto.Int32(0x0e844d0f)

		//a.clientPayload.UserAgent.PhoneId = proto.String("90196710-7a70-45cf-8d8e-364c40d79296")
	}
}

func (a *AccountInfo) GetStaticKeys() *DHKey {
	return &DHKey{
		Private: a.loginInfo.priKey,
		Public:  a.loginInfo.pubKey,
	}
}

func (a *AccountInfo) GetClientPayload() *waProto.ClientPayload {
	return a.clientPayload
}

// GenAuthDataService 生成认证数据
func GenAuthDataService(payload *waProto.ClientPayload) error {
	// check parameters
	if payload.GetUsername() == 0 || IsEmpty(payload.GetPushName()) {
		return errors.New("IncompleteParametersCode")
	}
	// check user agent
	userAgent := payload.GetUserAgent()
	if userAgent == nil || IsEmpty(userAgent.GetDevice()) ||
		IsEmpty(userAgent.GetOsBuildNumber()) ||
		IsEmpty(userAgent.GetManufacturer()) ||
		IsEmpty(userAgent.GetPhoneID()) ||
		IsEmpty(userAgent.GetOsVersion()) {
		return errors.New("IncompleteParametersCode")
	}
	// set other parameters
	payload.ShortConnect = proto.Bool(false)
	wifi := waProto.ClientPayload_WIFI_UNKNOWN
	payload.ConnectType = &wifi
	// set app version
	platform := userAgent.GetPlatform().String()
	//如果为安卓普通版
	if platform == "ANDROID" {
		payload.UserAgent.AppVersion.Primary = proto.Uint32(GetVersion42().Primary)
		payload.UserAgent.AppVersion.Secondary = proto.Uint32(GetVersion42().Secondary)
		payload.UserAgent.AppVersion.Tertiary = proto.Uint32(GetVersion42().Tertiary)
		payload.UserAgent.AppVersion.Quaternary = proto.Uint32(GetVersion42().Quaternary)
		payload.Oc = proto.Bool(true)
		payload.Lc = proto.Int32(1)
		payload.YearClass = proto.Int32(2016)
		payload.MemClass = proto.Int32(256)
	} else if platform == "SMB_ANDROID" {
		//安卓企业版
		payload.UserAgent.AppVersion.Primary = proto.Uint32(GetBusinessVersion42().Primary)
		payload.UserAgent.AppVersion.Secondary = proto.Uint32(GetBusinessVersion42().Secondary)
		payload.UserAgent.AppVersion.Tertiary = proto.Uint32(GetBusinessVersion42().Tertiary)
		payload.UserAgent.AppVersion.Quaternary = proto.Uint32(GetBusinessVersion42().Quaternary)
	} else {
		//return vo.AnErrorOccurred(fmt.Errorf("platform参数不正确%s", payload.UserAgent.Platform.String()))
		return errors.New(fmt.Sprintf("platform参数不正确%s", payload.UserAgent.Platform.String()))
	}
	fmt.Sprintln("platform参数=%s", payload.UserAgent.Platform.String())
	// set locale
	if payload.UserAgent.LocaleLanguageIso6391 == nil {
		payload.UserAgent.LocaleLanguageIso6391 = proto.String("zh")
	}
	if payload.UserAgent.LocaleCountryIso31661Alpha2 == nil {
		payload.UserAgent.LocaleCountryIso31661Alpha2 = proto.String("CN")
	}
	return nil
}
