package services

import (
	"errors"
	"github.com/betterde/ects/internal/build"
	"log"
	"strconv"
	"time"

	"github.com/go-xorm/builder"
	"github.com/kataras/iris/v12/middleware/jwt"

	"github.com/betterde/ects/config"
	"github.com/betterde/ects/internal/response"
	"github.com/betterde/ects/models"
)

type (
	UserInterface interface {
		Users(params map[string]string) (*[]models.User, *response.Meta)
		FindByID(id string) (*models.User, error)
		FindByEmail(email string) *models.User
		Attempt(username, password string) (string, error)
		Destroy(id string, force bool) error
	}

	UserService struct {
	}
)

func NewUserService() UserInterface {
	return &UserService{}
}

// Users Get users list
func (service *UserService) Users(params map[string]string) (*[]models.User, *response.Meta) {
	var (
		page  = 1
		limit = 10
		start int
		total int64
		err   error
	)

	if value, exist := params["page"]; exist == true {
		v, err := strconv.Atoi(value)
		if err != nil {

		}
		if v >= 0 {
			page = v
		}
	}

	if value, exist := params["limit"]; exist == true {
		v, err := strconv.Atoi(value)
		if err != nil {

		}
		if v >= 0 {
			limit = v
		}
	}

	start = (page - 1) * limit
	users := make([]models.User, 0)

	if search, exist := params["search"]; exist && search != "" {
		total, err = models.Engine.Where(builder.Like{"name", search}).Limit(limit, start).Count(&users)
	} else {
		total, err = models.Engine.Limit(limit, start).Count(&users)
	}

	if err != nil {
		log.Println(err)
	}

	return &users, &response.Meta{
		Limit: limit,
		Page:  page,
		Total: int(total),
	}
}

// Attempt user credentials
func (service *UserService) Attempt(username, passwod string) (token string, err error) {
	var user *models.User
	user, err = service.RetrieveByCredentials(username, passwod)

	if err != nil {
		return "", err
	}

	token, err = IssueToken(user)

	if err := models.CreateLog(user, user.Id, "USER SIGN IN"); err != nil {
		log.Println(err)
	}

	return token, err
}

// RetrieveByCredentials Get user by credentials
func (service *UserService) RetrieveByCredentials(username, password string) (user *models.User, err error) {
	user = service.FindByEmail(username)
	if user == nil {
		return nil, errors.New("用户不存在")
	}

	result, err := models.ValidatePassword(password, []byte(user.Password))

	if result {
		return user, err
	}

	return nil, errors.New("用户名或密码错误")
}

// FindByEmail Get user by email
func (service *UserService) FindByEmail(email string) *models.User {
	user := models.User{}
	result, err := models.Engine.Unscoped().Where(builder.Eq{"email": email}).Get(&user)
	if err != nil {
		log.Printf("%v", err)
	}

	if result {
		return &user
	}

	return nil
}

// FindByID Get user
func (service *UserService) FindByID(id string) (*models.User, error) {
	var user models.User
	result, err := models.Engine.Id(id).Get(&user)
	if err != nil {
		log.Println(err)
	}

	if result {
		return &user, nil
	}

	return &user, errors.New("用户不存在")
}

// Destroy Delete user
func (service *UserService) Destroy(id string, force bool) (err error) {
	var result int64
	if force {
		result, err = models.Engine.Id(id).Unscoped().Delete(&models.User{})
	} else {
		result, err = models.Engine.Id(id).Delete(&models.User{})
	}

	if err != nil && result > 0 {
		return nil
	}

	return errors.New("用户删除失败")
}

// IssueToken Issue access token
func IssueToken(user *models.User) (string, error) {
	secret := []byte(config.Conf.Auth.Secret)
	signer := jwt.NewSigner(jwt.HS256, secret, time.Duration(config.Conf.Auth.TTL)*time.Second)
	claims := jwt.Claims{
		Issuer:    build.Name,
		Expiry:    time.Now().Add(time.Second * time.Duration(config.Conf.Auth.TTL)).Unix(),
		Subject:   user.Id,
		IssuedAt:  time.Now().Unix(),
		NotBefore: time.Now().Unix(),
	}

	bytes, err := signer.Sign(&claims)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}
