package internal

import (
	"context"
	"strings"
	"yx-dataset-server/app/bll"
	"yx-dataset-server/app/errors"
	"yx-dataset-server/app/model"
	"yx-dataset-server/app/schema"
	"yx-dataset-server/library/auth"
	"yx-dataset-server/library/utils"

	"github.com/LyricTian/captcha"
	"net/http"
)

// NewLogin 创建Login
func NewLogin(
	Auth auth.Auther,
	user model.IUser,
	verifyCode bll.IVerifyCode,
	mTransModel model.ITrans,
	mLoginHis model.ILoginHistory,
) *Login {
	return &Login{
		auth:          Auth,
		userModel:     user,
		verifyCodeBll: verifyCode,
		transModel:    mTransModel,
		loginHisModel: mLoginHis,
	}
}

// Login 创建Login对象
type Login struct {
	auth          auth.Auther
	userModel     model.IUser
	verifyCodeBll bll.IVerifyCode
	transModel    model.ITrans
	loginHisModel model.ILoginHistory
}

// GetCaptchaID 获取图形验证码信息
func (a *Login) GetCaptchaID(ctx context.Context, length int) (*schema.LoginCaptcha, error) {
	captchaID := captcha.NewLen(length)
	item := &schema.LoginCaptcha{
		CaptchaID: captchaID,
	}
	return item, nil
}

// ResCaptcha 生成并响应图形验证码
func (a *Login) ResCaptcha(ctx context.Context, w http.ResponseWriter, captchaID string, width, height int) error {
	err := captcha.WriteImage(w, captchaID, width, height)
	if err != nil {
		return err
	}
	w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
	w.Header().Set("Pragma", "no-cache")
	w.Header().Set("Expires", "0")
	w.Header().Set("Content-Type", "image/png")
	return nil
}

// GenerateToken 生成令牌
func (a *Login) GenerateToken(ctx context.Context, userID string) (*schema.LoginTokenInfo, error) {
	tokenInfo, err := a.auth.GenerateToken(userID)
	if err != nil {
		return nil, errors.New400Response("生成令牌发生错误")
	}

	item := &schema.LoginTokenInfo{
		AccessToken: tokenInfo.GetAccessToken(),
		TokenType:   tokenInfo.GetTokenType(),
		ExpiresAt:   tokenInfo.GetExpiresAt(),
	}
	return item, nil
}

// DestroyToken 销毁令牌
func (a *Login) DestroyToken(ctx context.Context, tokenString string) error {
	err := a.auth.DestroyToken(tokenString)
	if err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// RefreshToken 刷新令牌
func (a *Login) RefreshToken(ctx context.Context) (*schema.LoginTokenInfo, error) {
	userid := GetUserID(ctx)
	if userid == "" {
		return nil, errors.ErrInvalidToken
	}
	token, err := a.GenerateToken(ctx, userid)
	if err != nil {
		return nil, errors.New400Response("刷新令牌发生错误")
	}
	return token, nil
}

// Verify 登录验证
func (a *Login) Verify(ctx context.Context, userName, password string) (*schema.User, error) {
	// 检查是否是超级用户
	root := GetRootUser()
	if userName == root.UserName && root.Password == password {
		return root, nil
	}

	result, err := a.userModel.Query(ctx, schema.UserQueryParam{
		UserName: userName,
	})
	if err != nil {
		return nil, err
	} else if len(result.Data) == 0 {
		return nil, errors.ErrInvalidUserName
	}
	item := result.Data[0]
	if item.Password != utils.SHA1HashString(strings.ToUpper(password)) {
		return nil, errors.ErrInvalidPassword
	} else if !item.Status {
		return nil, errors.ErrInvalidUser
	}

	return item, nil
}

// GetUserInfo 获取当前用户登录信息
func (a *Login) GetUserInfo(ctx context.Context) (*schema.UserLoginInfo, error) {
	userID := GetUserID(ctx)
	if isRoot := CheckIsRootUser(ctx); isRoot {
		root := GetRootUser()
		loginInfo := &schema.UserLoginInfo{
			UserName: root.UserName,
			RealName: root.RealName,
			Photo:    root.Photo,
		}
		return loginInfo, nil
	}

	user, err := a.userModel.Get(ctx, userID)
	if err != nil {
		return nil, err
	} else if user == nil {
		return nil, errors.ErrInvalidUser
	} else if user.Status {
		return nil, errors.ErrUserDisable
	}

	loginInfo := &schema.UserLoginInfo{
		UserName: user.UserName,
		RealName: user.RealName,
	}

	return loginInfo, nil
}

func (a *Login) getAndCheckUser(ctx context.Context, userID string, opts ...schema.UserOptions) (*schema.User, error) {
	user, err := a.userModel.Get(ctx, userID, opts...)
	if err != nil {
		return nil, err
	} else if user == nil {
		return nil, errors.ErrInvalidUser
	} else if !user.Status {
		return nil, errors.ErrUserDisable
	}
	return user, nil
}

// UpdatePassword 更新当前用户登录密码
func (a *Login) UpdatePassword(ctx context.Context, params schema.UpdatePasswordParam) error {
	userID := GetUserID(ctx)
	if CheckIsRootUser(ctx) {
		return errors.New400Response("超级管理员密码只能通过配置文件修改")
	}
	if params.OldPassword == "" {
		return errors.ErrUserNotEmptyPwd
	}
	user, err := a.getAndCheckUser(ctx, userID)
	if err != nil {
		return err
	} else if utils.SHA1HashString(strings.ToUpper(params.OldPassword)) != user.Password {
		return errors.ErrLoginInvalidOldPwd
	}

	params.NewPassword = utils.SHA1HashString(strings.ToUpper(params.NewPassword))
	return a.userModel.UpdatePassword(ctx, userID, params.NewPassword)
}

// Unsubscribe 用户注销
func (a *Login) Unsubscribe(ctx context.Context, userId, tokenStr string) error {
	err := ExecTrans(ctx, a.transModel, func(ctx context.Context) error {
		err := a.userModel.Delete(ctx, userId)
		if err != nil {
			return err
		}

		return a.DestroyToken(ctx, tokenStr)
	})
	return err
}

// LoginBySms 通过短信验证码登录
func (a *Login) LoginBySms(ctx context.Context, params schema.SmsLoginParam) error {

	checkResult, err := a.verifyCodeBll.Check(ctx, schema.VerifyCodeCheck{
		Tel:          params.Phone,
		BusinessType: params.BusinessType,
		Code:         params.Code,
	})
	if err != nil {
		return err
	}
	if !checkResult {
		return errors.ErrSmsCode
	}
	return nil
}
