package diagcontroller

import (
	"context"
	"os"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"

	observabilitypingcapcomv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"github.com/tidbcloud/observability/pkg/diagcontroller/config"
	"github.com/tidbcloud/observability/pkg/diagcontroller/resources"
	diagstorage "github.com/tidbcloud/observability/pkg/diagcontroller/storage"
	"github.com/tidbcloud/observability/pkg/diagcontroller/tasks"
)

var Scheme = runtime.NewScheme()

func init() {
	utilruntime.Must(clientgoscheme.AddToScheme(Scheme))
	utilruntime.Must(observabilitypingcapcomv1.AddToScheme(Scheme))
	//+kubebuilder:scaffold:scheme
}

// Controller is used to manage diag-engine and distribute diag tasks to different diag-engine
type Controller struct {
	conf      *config.DiagConfig
	resources []*ResourceController
	tasks     []*TaskController
}

// NewController creates a new diag controller
func NewController(conf *config.DiagConfig) (*Controller, error) {
	kubeconf := ctrl.GetConfigOrDie()
	kubeconf.QPS = 50
	kubeconf.Burst = 100
	kubeCli, err := client.New(kubeconf, client.Options{
		Scheme: Scheme,
	})
	if err != nil {
		log.Error("init kubernetes client failed", zap.Error(err))
		os.Exit(1)
	}

	log.Info("diag controller config", zap.Any("Config", conf))
	var store diagstorage.Storage
	switch conf.Provider {
	case config.ProviderAWS:
		store, err = diagstorage.NewS3Storage(conf.Region)
		if err != nil {
			return nil, errors.Annotate(err, "init s3 storage failed")
		}
	case config.ProviderGCP:
		store, err = diagstorage.NewGCSStorage()
		if err != nil {
			return nil, errors.Annotate(err, "init gcs storage failed")
		}
	case config.ProviderAzure:
		store, err = diagstorage.NewAzureBlobStorage(conf.Task.StorageAccount)
		if err != nil {
			return nil, errors.Annotate(err, "init azure storage failed")
		}
	case config.ProviderAliCloud:
		store, err = diagstorage.NewOSSStorage(conf.Region)
		if err != nil {
			return nil, errors.Annotate(err, "init azure storage failed")
		}
	default:
		return nil, errors.Errorf("unknown provider %s", conf.Provider)
	}

	return &Controller{
		conf: conf,
		resources: []*ResourceController{
			NewResourceController(resources.NewDiagEngine(kubeCli, conf, store)),
		},
		tasks: []*TaskController{
			NewTaskController(tasks.NewDiagTask(kubeCli, conf, store)),
		},
	}, nil
}

func (c *Controller) Start(ctx context.Context) {
	for _, res := range c.resources {
		go res.Start(ctx)
	}
	for _, task := range c.tasks {
		go task.Start(ctx)
	}
}
