package sdk

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(*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,
	// if `apply` is `true` (and without `dryrun`), it will update the TidbCluster in K8s API server and this may cause cluster rolling-update.
	// In order to avoid conflict when updating to K8s API server, the caller should pass the latest TidbCluster object into this method.
	// The caller should requeue the resource and thus re-update config if the returned error is not nil.
	// NOTE: for PD, some config items can only be updated by the PD client, so they will be updated even `apply` is `false`.
	UpdateConfig(cluster *cloudv1.Cluster, revFinder RevisionFinder, tc *v1alpha1.TidbCluster, tcClient client.Client, pdCli DynamicPDClient,
		comps []cloudv1.ComponentType, apply bool) 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 {
	GetConfig() (map[string]interface{}, error)
	PutConfig(conf map[string]interface{}) 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
	}
}
