package authprovider

import (
	"errors"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/business/auth2"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
	"git.rosy.net.cn/jx-callback/globals/api"
)

const (
	DefVerifyCodeDuration = 5 * time.Minute

	VerifyCodeHeader = "VC"
	VerifyCodeVer    = "V2"
	TokenTypeSep     = "."
)

type DefAuther struct {
}

// 此函数为空
func (a *DefAuther) AddAuthBind(authBindEx *auth2.AuthBindEx, userName string) (err error) {
	authBind := &authBindEx.AuthBind
	dao.WrapAddIDCULDEntity(authBind, userName)
	authBind.Status = model.AuthBindStatusNormal
	err = dao.CreateEntity(nil, authBind)
	return err
}

func (a *DefAuther) UnbindAuth(userID, authType, authTypeID, userName string) (err error) {
	condition := map[string]interface{}{
		"UserID":             userID,
		"Type":               authType,
		model.FieldDeletedAt: utils.DefaultTimeValue,
	}
	if authTypeID != "" {
		condition["TypeID"] = authTypeID
	}
	_, err = dao.DeleteEntityLogically(dao.GetDB(), &model.AuthBind{}, nil, userName, condition)
	return err
}

func (a *DefAuther) SendVerifyCode(authID string) (verifyCode string, err error) {
	return "", errors.New("当前登录类型不支持此操作")
}

// 此函数为空
func (a *DefAuther) Logout(authInfo *auth2.AuthInfo) error {
	return nil
}

func (a *DefAuther) GetUserType() (userType int8) {
	return model.UserTypeConsumer
}

// 此函数用于联合（通过unionID）查找用户
func (a *DefAuther) UnionFindAuthBind(curAuthType, curAuthTypeID string, unionAuthTypeList []string, openID, unionID string, authDetail interface{}) (authBindEx *auth2.AuthBindEx, err error) {
	db := dao.GetDB()
	var authBind *model.AuthBind
	if authBind, err = dao.GetAuthBind(db, model.AuthBindTypeAuth, curAuthType, openID); err == nil { // 直接找到了
		authBindEx = &auth2.AuthBindEx{
			AuthBind: *authBind,
		}
		if authDetail != nil {
			authBindEx.DetailData = string(utils.MustMarshal(authDetail))
		}
		if authBind.TypeID == "" {
			authBind.TypeID = curAuthTypeID
			dao.UpdateEntity(db, authBind, "TypeID")
		}
	} else if dao.IsNoRowsError(err) { // 直接找不到，尝试unionID
		if unionID != "" || openID != "" { // 且有unionID
			var authBindList []*model.AuthBind
			if authBindList, err = dao.GetUserBindAuthInfo(db, "", model.AuthBindTypeAuth, unionAuthTypeList, openID, unionID, nil); err == nil && len(authBindList) > 0 { // 通过unionID找到至少一个认证方式
				authBind = authBindList[0]
				authBind.Type = curAuthType
				authBind.TypeID = curAuthTypeID
				authBind.AuthID = openID
				if authDetail != nil {
					authBind.DetailData = string(utils.MustMarshal(authDetail))
				}
				authBindEx = &auth2.AuthBindEx{
					AuthBind: *authBind,
				}
				a.UnbindAuth(authBind.UserID, curAuthType, curAuthTypeID, model.AdminName)
				err = a.AddAuthBind(authBindEx, model.AdminName) // 自动绑定
			} else if dao.IsNoRowsError(err) {
				err = nil
			}
		} else {
			err = nil
		}
	}
	if err == nil && authBindEx == nil {
		//如果没有报错，且没有找到一个认证方式，创建无用户（UserID为空）的认证方式
		authBindEx = &auth2.AuthBindEx{
			AuthBind: model.AuthBind{
				Type:    curAuthType,
				TypeID:  curAuthTypeID,
				AuthID:  openID,
				AuthID2: unionID,
			},
		}
		if authDetail != nil {
			authBindEx.DetailData = string(utils.MustMarshal(authDetail))
		}
	}
	return authBindEx, err
}

// cache相关

func (a *DefAuther) SaveVerifyCode(keyID, verifyCode string) {
	api.Cacher.Set(a.buildCacheKey4Verify(keyID), verifyCode, DefVerifyCodeDuration)
}

func (a *DefAuther) LoadVerifyCode(keyID string) (verifyCode string) {
	if value := api.Cacher.Get(a.buildCacheKey4Verify(keyID)); value != nil {
		return value.(string)
	}
	return ""
}

func (a *DefAuther) DeleteVerifyCode(keyID string) {
	api.Cacher.Del(a.buildCacheKey4Verify(keyID))
}

// cache相关

func (a *DefAuther) GenerateVerifyCode(keyID string) (verifyCode string) {
	verifyCode = a.LoadVerifyCode(keyID)
	if verifyCode == "" {
		verifyCode = fmt.Sprintf("%06d", rand.Intn(1000000))
	}
	return verifyCode
}

func (a *DefAuther) VerifyCode(keyID, verifyCode string) (isSame bool) {
	if keyID != "" {
		savedVerifyCode := a.LoadVerifyCode(keyID)
		if isSame = (verifyCode != "" && savedVerifyCode != "" && verifyCode == savedVerifyCode); isSame {
			a.DeleteVerifyCode(keyID)
		}
	}
	return isSame
}

func (a *DefAuther) buildCacheKey4Verify(keyID string) string {
	return strings.Join([]string{
		VerifyCodeHeader,
		VerifyCodeVer,
		keyID,
	}, TokenTypeSep)
}
