package apikey

import (
	"context"
	"errors"
	"strings"

	"github.com/tidbcloud/account/central/utils"

	"github.com/tidbcloud/account/commons/database"
	"github.com/tidbcloud/account/internel/dao"

	"gorm.io/gorm"

	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/tiinf/errs"
)

var apiKeySupportScopeType = map[entity.ScopeType]struct{}{
	entity.RoleBindingScopeOrg:     {},
	entity.RoleBindingScopeDataAPP: {},
	entity.RoleBindingScopeProject: {},
	entity.RoleBindingScopeTiDB:    {},
}

func (s *ApiKeySvc) ParseResourceRefScopeType(scopeTypeStr string) (entity.ScopeType, error) {
	upperScopeTypeStr := strings.ToUpper(scopeTypeStr)
	scopeType := entity.ParseScopeType(upperScopeTypeStr)
	if scopeType == "" {
		return "", errs.NewForbidden("scope_type invalid", nil)
	}
	_, ok := apiKeySupportScopeType[scopeType]
	if !ok {
		return "", errs.NewForbidden("scope_type not support", nil)
	}
	return scopeType, nil
}

func (s *ApiKeySvc) ParseResourceRefScopeTypeAndRole(scopeTypeStr, roleStr string) (entity.ScopeType, *entity.RbacRole, error) {
	scopeType, err := s.ParseResourceRefScopeType(scopeTypeStr)
	if err != nil {
		return "", nil, err
	}

	if roleStr == entity.RoleApiKeyOrgOwner {
		// api:org:owner is deprecated, will use org:owner later
		roleStr = entity.RoleOrgOwner
	}
	role, err := dao.GetDaoHub().Rbac.GetRbacRoleByName(context.Background(), database.DBConn.DB, roleStr)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return "", nil, errs.NewBadRequest("role invalid", err)
		}
		return "", nil, err
	}

	if !IsValidApiKeyRole(scopeTypeStr, roleStr) {
		return "", nil, errs.NewBadRequest("scope_type and role not match", nil)
	}

	return scopeType, role, nil
}

func IsValidApiKeyRole(scopeType string, role string) bool {
	if scopeType == entity.RoleBindingScopeOrg.String() {
		if role == entity.RoleApiKeyOrgOwner || role == entity.RoleBearerTokenApiKeyScimOwner {
			return true
		}
		err := utils.IsValidUserOrgRole(role)
		return err == nil
	}
	if scopeType == entity.RoleBindingScopeProject.String() {
		err := utils.IsValidUserProjectRole(role)
		return err == nil
	}
	return true
}

const (
	digestLength = 4
)

func GetKeyDigest(privateKey string) string {
	if len(privateKey) > digestLength {
		return privateKey[len(privateKey)-digestLength:]
	}

	return ""
}
