package configmanager

import (
	"context"

	"github.com/pingcap/errors"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"

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

type RevisionFinder func(c *cloudv1.Cluster) (*cloudv1.ConfigRevision, error)

type Interface interface {
	// InitConfig initialize the cluster config in TidbCluster resource before cluster creation,
	// this function should not be called after creation since some configs
	// cannot be changed via tidbcluster spec after creation
	InitConfig(cluster *cloudv1.Cluster, revFinder RevisionFinder, tc *v1alpha1.TidbCluster) error
	// UpdateConfig updates the specific components config in TidbCluster resource after cluster creation.
	// Excluding PD, the UpdateConfig only update the configuration from ConfigRevision to TC in memory. The caller
	// is responsible for updating the updated TC to k8s api-server.
	// The caller should requeue the resource and thus re-update config if the returned error is not nil.
	UpdateConfig(ctx context.Context, cluster *cloudv1.Cluster, regionalManaged bool, revFinder RevisionFinder, tc *v1alpha1.TidbCluster, pdCli DynamicPDClient,
		comps []cloudv1.ComponentType) error

	InitBackupConfig(cluster *cloudv1.Cluster, revFinder RevisionFinder, backup *v1alpha1.Backup) error

	InitRestoreConfig(cluster *cloudv1.Cluster, revFinder RevisionFinder, restore *v1alpha1.Restore) error

	// TODO(just1900): support lightning config management
	// InitLightningConfig(cluster *cloudv1.Cluster, lightning *corev1.Container, conf *cloudv1.TomlConfig) error
}

type DynamicPDClient interface {
	CancelDeleteStore(ctx context.Context, storeID string) error
	RemoveEvictLeaderScheduler(ctx context.Context) error
	GetEvictLeaderSchedulers(ctx context.Context) ([]string, error)
	GetStores(ctx context.Context) (StoresInfo, error)
	GetConfig(ctx context.Context) (map[string]interface{}, error)
	PutConfig(ctx context.Context, conf map[string]interface{}) error
	HTTPGet(ctx context.Context, path string) ([]byte, error)
	HTTPPost(ctx context.Context, path string, body []byte) error
	HTTPDelete(ctx context.Context, path string, body []byte) error
}

var RevFinderBuilder = func(cli client.Client) RevisionFinder {
	return func(c *cloudv1.Cluster) (*cloudv1.ConfigRevision, error) {
		if c.Spec.ConfigRevisionRef == nil {
			return nil, errors.Errorf("cluster %s/%s has no config revision ref", c.Namespace, c.Name)
		}
		rev := &cloudv1.ConfigRevision{}
		err := cli.Get(context.TODO(), types.NamespacedName{Name: c.Spec.ConfigRevisionRef.Name}, rev)
		if err != nil {
			return nil, err
		}
		return rev, nil
	}
}
