package auth

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/pkgv2/secret/encrypt"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
	"go.uber.org/zap"
)

const TestTokenPrefix = "CLOUD"

type Claims struct {
	Email      string
	ExpireTime int64
}

type TestTokenChecker struct {
	aesCrypt *encrypt.AESCrypt
}

func NewTestTokenChecker(saltSecret string) (*TestTokenChecker, error) {
	aesCrypt, err := encrypt.NewAESCrypt([]byte(saltSecret))
	if err != nil {
		return nil, errors.Trace(err)
	}
	return &TestTokenChecker{aesCrypt}, nil
}

func (m *TestTokenChecker) TryGetTestUserFromToken(
	ctx context.Context, accountFacade facade.AccountFacade,
	rawToken string, orgId uint64, skipCheckOrg bool,
) *facade.User {
	email := m.TryGetEmailFromTestToken(ctx, rawToken)
	if email == "" {
		return nil
	}
	user, found, err := accountFacade.GetAuthUserInOrg(ctx, email, orgId, skipCheckOrg)
	if err != nil {
		return nil
	}
	if found {
		return user
	}
	log.WithContextL(ctx).Warn("get test user failed", zap.String("email", email))
	return nil
}

func (m *TestTokenChecker) TryGetEmailFromTestToken(ctx context.Context, rawToken string) string {
	if strings.HasPrefix(rawToken, TestTokenPrefix) {
		newToken := strings.TrimPrefix(rawToken, TestTokenPrefix)
		claims := Claims{}
		newTokenData, err := base64.URLEncoding.DecodeString(newToken)
		logger := log.WithContextL(ctx)
		if err != nil {
			logger.Error("decode token failed", zap.String("token", rawToken), zap.Error(err))
			return ""
		}
		if err := json.Unmarshal(m.aesCrypt.Decrypt(newTokenData), &claims); err != nil {
			logger.Error("parse token failed", zap.String("token", rawToken), zap.Error(err))
			return ""
		}
		if claims.ExpireTime < time.Now().Unix() {
			logger.Info("token is expired", zap.Int64("time", claims.ExpireTime))
			return ""
		}
		return claims.Email
	}
	return ""
}
