package util

import (
	"context"
	"fmt"

	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

// ClusterReader knows how to get a Cluster CR based on cluster ID.
type ClusterReader interface {
	Get(ctx context.Context, cli client.Client, clusterId string) (*cloudv1.Cluster, error)
}

var _ ClusterReader = &LabelSelectorReader{}

// LabelSelectorReader get a Cluster CR via LIST with label selectors.
type LabelSelectorReader struct{}

func (r *LabelSelectorReader) Get(ctx context.Context, cli client.Client, clusterId string) (*cloudv1.Cluster, error) {
	var list cloudv1.ClusterList
	if err := cli.List(ctx, &list, client.MatchingLabelsSelector{
		Selector: labels.Set{
			cloudv1.ClusterTagKey: clusterId,
		}.AsSelector(),
	}); err != nil {
		return nil, fmt.Errorf("failed to list clusters: %w", err)
	}

	if len(list.Items) == 0 {
		return nil, errors.NewNotFound(schema.GroupResource{Group: "cloud.pingcap.com", Resource: "clusters"}, clusterId)
	}

	if len(list.Items) > 1 {
		return nil, fmt.Errorf("found more than one cluster with label %s=%s", cloudv1.ClusterTagKey, clusterId)
	}

	return &list.Items[0], nil
}

// NameReader get a Cluster CR via GET directly if the Cluster CRD is cluster scope.
type NameReader struct{}

var _ ClusterReader = &NameReader{}

func (r *NameReader) Get(ctx context.Context, cli client.Client, clusterId string) (*cloudv1.Cluster, error) {
	var cluster cloudv1.Cluster
	if err := cli.Get(ctx, client.ObjectKey{Name: clusterId}, &cluster); err != nil {
		return nil, err
	}
	return &cluster, nil
}
