package utils

import (
	"context"
	"errors"
	"fmt"

	"github.com/tidbcloud/pkgv2/auth/auth0"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/serverless-service/internal/config"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
	"go.uber.org/zap"
)

const (
	UnknownIdentityKey = "unknown"
	UnknownUserIDKey   = "unknown"
)

func GetUserFromContext(ctx context.Context) (*facade.User, error) {
	claimsIFace := ctx.Value(auth0.AuthTokenClaimKey)
	if claimsIFace == nil {
		return nil, errors.New("user not in context")
	}
	user, ok := claimsIFace.(*facade.User)
	if !ok {
		return nil, errors.New("expect type facade.User in context")
	}
	return user, nil
}

func GetIdentityKeyFromContext(ctx context.Context) string {
	identityKey := ctx.Value(config.CtxIdentityKey)
	if identityKey == nil || identityKey == "" {
		return UnknownIdentityKey
	}
	return identityKey.(string)
}

func GetAuthUserIDKeyFromContext(ctx context.Context) string {
	useIDKey := ctx.Value(auth0.AuthUserIDKey)
	if useIDKey == nil || useIDKey == "" {
		return UnknownUserIDKey
	}
	return useIDKey.(string)
}

func GetAuthTypeFromContext(ctx context.Context) string {
	authType := ctx.Value(auth0.AuthTypeKey)
	if authType == nil {
		return config.MailAuthType
	}
	r, ok := authType.(string)
	if !ok {
		return config.MailAuthType
	}
	return r
}

func GetGrpcGatewayValueFromContext(ctx context.Context) string {
	grpcGatewayValue := ctx.Value(config.CtxGRPCGatewayTokenKey)
	if grpcGatewayValue == nil {
		return ""
	}
	r, ok := grpcGatewayValue.(string)
	if !ok {
		return ""
	}
	return r
}

func GetWorkerLoggerFromContext(ctx context.Context) *zap.Logger {
	logger := log.WithContextL(ctx)
	workerName := ctx.Value(config.CtxWorkerNameKey)
	if workerName == nil {
		return logger
	}
	if worker, ok := workerName.(string); ok {
		return logger.With(zap.String("worker", worker))
	}
	return logger
}

func GetEventBusLoggerFromContext(ctx context.Context) *zap.Logger {
	logger := log.WithContextL(ctx)
	workerName := ctx.Value(config.CtxWorkerNameKey)
	if workerName != nil {
		if worker, ok := workerName.(string); ok {
			logger = logger.With(zap.String("worker", worker))
		}
	}

	eventID := ctx.Value(config.CtxEventIDKey)
	if eventID == nil {
		return logger
	}
	if id, ok := eventID.(string); ok {
		return logger.With(zap.String("eventID", id))
	}
	return logger
}

func BuildCreatorID(ctx context.Context) string {
	logger := log.WithContextL(ctx)
	// openapi and console
	authType := GetAuthTypeFromContext(ctx)
	if authType == config.MailAuthType || authType == config.AccessTokenAuthType {
		return GetAuthUserIDKeyFromContext(ctx)
	}
	if authType == config.ApiKeyAuthType {
		return fmt.Sprintf("apikey-%s", GetIdentityKeyFromContext(ctx))
	}

	logger.Warn("unknown auth type", zap.String("authType", authType))
	return UnknownUserIDKey
}
