package usecases

import (
	"cargo-erp-backend/internal/domain"
	"cargo-erp-backend/pkg/helpers"
	"cargo-erp-backend/pkg/jwt"
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"
)

type AuthUsecaseInterface interface {
	Login(email string, password string) (token string, err error)
}
type AuthUsecase struct {
	Helper helpers.HelperInterface
}

func NewAuthUsecase(helper helpers.HelperInterface) AuthUsecaseInterface {
	return &AuthUsecase{
		Helper: helper,
	}
}
func (u *AuthUsecase) Login(email string, password string) (token string, err error) {
	var user []domain.User
	db := u.Helper.GetDB("slave")
	if err := db.Model(&user).Where("email=?", email).Preload("UsersRole_Relation").Find(&user).Error; err != nil {
		return "", fmt.Errorf("User Not Found")
	}
	for _, v := range user {
		pass := u.Helper.StringFromPtr(v.Password)
		if u.Helper.VerifyPassword(pass, password) {
			ctx := context.Background()
			rd := u.Helper.GetRedis("master")
			secretJwt := jwt.NewJWT(u.Helper.Config(), u.Helper.Log())
			token, err := secretJwt.GenerateToken(v.ID)
			if err != nil {
				return "", err
			}
			extime_str := fmt.Sprintf("%v", u.Helper.Config().Get("jwt.expiry_time"))
			t, err := strconv.Atoi(extime_str)
			if err != nil {
				return "", err
			}
			//
			key := fmt.Sprintf("session_login:%v:*", v.ID)
			iter := u.Helper.GetRedis("master").Scan(ctx, 0, key, 1000).Iterator()
			for iter.Next(ctx) {
				keys := iter.Val()
				keys_ex := strings.Split(keys, ":")
				if keys_ex[1] == v.ID {
					cachedToken := keys_ex[2]
					_, verifyErr := secretJwt.VerifyToken(cachedToken)
					if verifyErr == nil {
						u.Helper.GetRedis("master").Expire(ctx, keys, time.Duration(t)*time.Second)
						return cachedToken, nil
					}
					u.Helper.GetRedis("master").Del(ctx, keys)
				}
			}

			key = fmt.Sprintf("session_login:%v:%v", v.ID, token)
			clonner, err := u.Helper.CloneStructWithoutField(v, "password")
			if err != nil {
				return "", err
			}
			bdata, _ := u.Helper.StructTobyte(clonner)
			status := rd.Set(ctx, key, bdata, time.Duration(t)*time.Second)
			if status.Err() != nil {
				return "", status.Err()
			}
			return token, nil
		}
	}
	return "", fmt.Errorf("User Not Found")
}
