package auth

import (
	"context"
	"fmt"

	"braces.dev/errtrace"
	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	authsdk "github.com/tidbcloud/tiinf/sdk/auth"
	"go.uber.org/zap"

	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/domain/entity"
)

func (a *authServiceImpl) getProjectsByApiKeyScope(
	ctx context.Context,
	apiKeyRsp *account.GetApiKeyByAccessKeyRsp,
) ([]*account.Project, error) {
	return getProjectsByAPIKeyScope(ctx, a.account, apiKeyRsp)
}

func (a *authServiceImpl) getCurrentUserByAPIKey(
	ctx *gin.Context,
	oaAccess *account.GetApiKeyByAccessKeyRsp,
) (*entity.User, error) {
	if oaAccess == nil || oaAccess.ApiKey == nil || len(oaAccess.ResourceInfos) == 0 {
		tilog.Error(ctx.Request.Context(), "failed to get open api access")
		return nil, errtrace.Wrap(fmt.Errorf("failed to retrieve open api access"))
	}

	orgId, err := authsdk.GetOrgIdFromApiKeyRsp(ctx.Request.Context(), oaAccess)
	if err != nil {
		tilog.Error(
			ctx.Request.Context(),
			"failed to get org id of public key",
			zap.String("publicKey", oaAccess.ApiKey.GetAccessKey()),
			zap.Error(err),
		)
		return nil, fmt.Errorf("failed to get org id public key: %s, %w", oaAccess.ApiKey.GetAccessKey(), err)
	}

	projects, err := a.getProjectsByApiKeyScope(ctx.Request.Context(), oaAccess)
	if err != nil {
		tilog.Error(
			ctx.Request.Context(),
			"failed to get projects of public key",
			zap.String("publicKey", oaAccess.ApiKey.GetAccessKey()),
			zap.Error(err),
		)
		return nil, fmt.Errorf("failed to get projects public key: %s, %w", oaAccess.ApiKey.GetAccessKey(), err)
	}

	u, err := a.account.GetOrCreateSystemUser(ctx, orgId)
	if err != nil {
		tilog.Error(ctx.Request.Context(), "failed to get or create system user", zap.Error(err))
		return nil, errtrace.Wrap(fmt.Errorf("failed to get or create system user: %w", err))
	}

	user := &entity.User{
		User:         u,
		PublicKey:    oaAccess.ApiKey.GetAccessKey(),
		PublicKeyId:  oaAccess.ApiKey.GetId(),
		Projects:     projects,
		Org:          &account.Org{Id: orgId},
		IdentityType: authsdk.AuthnIdentityTypeApiKeyAK,
	}

	if len(user.Projects) == 0 {
		tilog.Error(
			ctx.Request.Context(),
			"no project in this organization",
			zap.String("publicKey", oaAccess.ApiKey.GetAccessKey()),
		)
		return nil, fmt.Errorf("no project in this organization")
	}

	return user, nil
}
