package server

import (
	"errors"
	"fmt"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-server/v1"
)

var (
	errPremiumReplayMissingTargetEndpoint   = errors.New("target endpoint is not set")
	errPremiumReplayMissingSourceAuditLog   = errors.New("source audit log is not set for premium replay")
	errPremiumReplayMissingAWSAuditLogS3URL = errors.New("aws audit log s3 url is not set")
	errReplayAWSAuditLogMissingSource       = errors.New("aws audit log requires s3_url or role_arn and external_id")
	errReplayAWSAuditLogCredentialMismatch  = errors.New("aws audit log role_arn and external_id must be set together")
	errReplayCloudClusterMissing            = errors.New("cloud cluster is not set")
	errReplaySourceClusterMissing           = errors.New("source cluster is not set")
	errReplayTargetClusterMissing           = errors.New("target cluster is not set")
	errReplaySourceClusterProjectMissing    = errors.New("source cluster project is not set")
	errReplaySourceAuditLogProviderMismatch = errors.New("provider mismatch")
	errReplayDataplaneMissingDataNode       = errors.New("data node is not set")
	errReplayDataplaneUnsupportedPlacement  = errors.New("not supported deploy position for filling dataplane info automatically")
	errReplayInvalidDataplaneKubernetes     = errors.New("invalid dataplane_kubernetes")
	errReplayUnsupportedClusterType         = errors.New("unsupported cluster type")
	errReplayUnsupportedRouteStrategy       = errors.New("unsupported route strategy")
	errReplayUnsupportedCloudProvider       = errors.New("unsupported cloud provider")
)

var replayBuildInvalidArgumentSentinels = []error{
	errPremiumReplayMissingTargetEndpoint,
	errPremiumReplayMissingSourceAuditLog,
	errPremiumReplayMissingAWSAuditLogS3URL,
	errReplayAWSAuditLogMissingSource,
	errReplayAWSAuditLogCredentialMismatch,
	errReplayCloudClusterMissing,
	errReplaySourceClusterMissing,
	errReplayTargetClusterMissing,
	errReplaySourceClusterProjectMissing,
	errReplaySourceAuditLogProviderMismatch,
	errReplayDataplaneMissingDataNode,
	errReplayDataplaneUnsupportedPlacement,
	errReplayInvalidDataplaneKubernetes,
	errReplayUnsupportedClusterType,
	errReplayUnsupportedRouteStrategy,
	errReplayUnsupportedCloudProvider,
}

func replayBuildError(prefix string, err error) error {
	if err == nil {
		return nil
	}
	if s, ok := status.FromError(err); ok && s.Code() != codes.Unknown {
		return err
	}
	if isReplayBuildInvalidArgument(err) {
		return status.Errorf(codes.InvalidArgument, "%s: %s", prefix, err)
	}
	return status.Errorf(codes.Internal, "%s: %s", prefix, err)
}

func isReplayBuildInvalidArgument(err error) bool {
	for _, sentinel := range replayBuildInvalidArgumentSentinels {
		if errors.Is(err, sentinel) {
			return true
		}
	}
	return false
}

func unsupportedReplayClusterTypeInScope(clusterType any, scope string) error {
	return fmt.Errorf("%w %v for %s", errReplayUnsupportedClusterType, clusterType, scope)
}

func unsupportedReplayClusterType(clusterType any) error {
	return unsupportedReplayClusterTypeInScope(clusterType, "replay")
}

func unsupportedReplayCloudProvider(provider string) error {
	return fmt.Errorf("%w %s for replay", errReplayUnsupportedCloudProvider, provider)
}

func unsupportedReplayRouteStrategy(sourceType, targetType apiv1.Replay_ClusterType) error {
	return fmt.Errorf("%w: source cluster type %s and target cluster type %s", errReplayUnsupportedRouteStrategy, sourceType, targetType)
}

func replayMissingDataNode(prefix string) error {
	if prefix == "" {
		return errReplayDataplaneMissingDataNode
	}
	return fmt.Errorf("%s %w", prefix, errReplayDataplaneMissingDataNode)
}

func premiumReplayMissingDataNode() error {
	return replayMissingDataNode("premium")
}
