package core

import (
	"context"
	"github.com/guo-shaoge/tiflash-autoscale/pkg/util/log/logger"
	"go.uber.org/zap"
	"sync"
)

// background workers:
// 1. configMgr: to update config.
// 2. warmupPoolMgr: to keep pool size fixed.
// 3. analyzeMgr: main policy implementation, will start one goroutine for each tenant.
// 4. podWatcher: detect CloneSet change(add pod/update pod/del pod).
// 5. podScanner: scan all pods to check its status.
type ClusterMgr struct {
	ctx context.Context
	wg  *sync.WaitGroup

	configMgr  *ConfigMgr
	podMgr     *PodMgr
	tenantMgr  *TenantMgr
	k8sMgr     *K8sMgr
	metricMgr  *MetricMgr
	httpServer *HttpServer

	rootLogger *zap.Logger
}

func NewClusterMgr(parentCtx context.Context, configPath string) *ClusterMgr {
	ctx, _ := context.WithCancel(parentCtx)

	configMgr := NewConfigMgr(ctx, configPath)
	if configMgr == nil {
		panic("create configMgr failed")
	}

	logPath := *configMgr.GetConfig().GetClusterConfig().LogPath
	logLevel := logger.GetLogLevel(*configMgr.GetConfig().GetClusterConfig().LogLevel)
	rootLogger, err := logger.InitLogger(logPath, logLevel)
	if err != nil {
		panic(err)
	}
	rootLogger.Info("Welcome to AutoScaler")
	configMgr.SetupLogger(rootLogger)
	configMgr.LogConfig()

	k8sMgr := NewK8sMgr(ctx, configMgr, rootLogger)
	if k8sMgr == nil {
		panic("create k8sMgr failed")
	}

	podMgr := NewPodMgr(ctx, k8sMgr, configMgr, rootLogger)
	if podMgr == nil {
		panic("create podMgr failed")
	}
	k8sMgr.SetPodMgr(podMgr)

	tenantMgr := NewTenantMgr(ctx, podMgr, configMgr, rootLogger)
	if tenantMgr == nil {
		panic("create tenantMgr failed")
	}

	metricMgr := NewMetricMgr(ctx, podMgr, configMgr, rootLogger)
	if metricMgr == nil {
		panic("create metricMgr failed")
	}

	httpServer := NewHttpServer(ctx, tenantMgr, podMgr, configMgr, rootLogger)
	if httpServer == nil {
		panic("create httpServer failed")
	}

	return &ClusterMgr{
		ctx: ctx,
		wg:  &sync.WaitGroup{},

		configMgr:  configMgr,
		podMgr:     podMgr,
		tenantMgr:  tenantMgr,
		k8sMgr:     k8sMgr,
		metricMgr:  metricMgr,
		httpServer: httpServer,

		rootLogger: rootLogger,
	}
}

func (c *ClusterMgr) Start() {
	c.configMgr.startBgWorker(c.wg)
	c.podMgr.startBgWorker(c.wg)
	c.tenantMgr.startBgWorker(c.wg)
	c.k8sMgr.startBgWorker(c.wg)
	c.metricMgr.startBgWorker(c.wg)
	c.httpServer.startBgWorker(c.wg)
}

func (c *ClusterMgr) Wait() {
	c.wg.Wait()
}

func (c *ClusterMgr) GetConfigMgr() *ConfigMgr {
	return c.configMgr
}

func (c *ClusterMgr) GetTenantMgr() *TenantMgr {
	return c.tenantMgr
}
