package cmd

import (
	"context"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/pingcap/log"
	"github.com/spf13/cobra"
	"github.com/tidbcloud/metrics-integration/global/internal/domain/cmd/bootstrap"
	"github.com/tidbcloud/metrics-integration/global/internal/domain/event_bus"
	"github.com/tidbcloud/metrics-integration/global/internal/domain/event_bus/consumer"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/config"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/tiinf/monitor/titracing"
	"github.com/tidbcloud/tiinf/sdk/auditlog"
	"go.uber.org/zap"
)

var (
	internalGRPCPort string
	gatewayPort      string
)

func init() {
	cmd := &cobra.Command{
		Use:   "server",
		Short: "start metrics integration server",
		Run: func(cmd *cobra.Command, args []string) {
			startServer()
		},
	}
	cmd.Flags().StringVar(&internalGRPCPort, "grpc-port", "8081", "internal gRPC server port")
	cmd.Flags().StringVar(&gatewayPort, "gateway-port", "8080", "gRPC gateway server port")
	rootCmd.AddCommand(cmd)
}

func startServer() {
	if err := setupAuditLogSDK(); err != nil {
		panic(err)
	}

	logger, props, err := log.InitLogger(&log.Config{})
	if err != nil {
		panic(err)
	}
	log.ReplaceGlobals(logger, props)
	log.Info("Starting metrics integration global server...")
	defer log.Sync() // nolint: errcheck

	// Create context that listens for the interrupt signal from the OS.
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	// init tracing even if the tracing is currently not usable in regional, because we need to init Propagator
	// to extra some info from HTTP header.
	if _, err = titracing.InitJaeger("metrics-integration"); err != nil {
		log.Error("init tiinf jaeger provider failed", zap.Error(err))
	}

	var wg sync.WaitGroup

	// Initialize eventbus
	if config.Global().ServerConfig.EnableEventBusConsumer {
		wg.Add(1)
		go func() {
			defer wg.Done()
			event_bus.RegisterHandlers()
			consumer.RegisterConsumer()
		}()
	}

	// Initialize gRPC server
	internalGRPCAddr := ":" + internalGRPCPort
	gatewayAddr := ":" + gatewayPort
	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Info("setupAndRunGRPCServer")
		bootstrap.SetupAndRunGRPCServer(ctx, internalGRPCAddr)
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Info("setupAndRunGRPCGateway")
		bootstrap.SetupAndRunGRPCGateway(ctx, gatewayAddr, internalGRPCAddr)
	}()

	wg.Wait()
	log.Info("metrics integration global server stopped")
}

func setupAuditLogSDK() error {
	if config.Global().DebugConfig.Enable {
		if err := auditlog.InitMockAuditLogSDK(context.TODO(), time.Second); err != nil {
			return bizerr.WrapError(err, "failed to init mock audit log sdk")
		}
		return nil
	}

	if err := auditlog.InitAuditLogSDK(context.TODO(), time.Second); err != nil {
		return bizerr.WrapError(err, "failed to init console audit log sdk")
	}
	return nil
}
