package main

import (
	"context"
	"database/sql"
	"dataservices/commons/redisutil"
	"dataservices/commons/sentry"
	"flag"
	"fmt"

	"github.com/tidbcloud/pkgv2/version"

	"dataservices/api"
	"dataservices/commons/apikeygenerator"
	"dataservices/commons/config"
	"dataservices/commons/database"
	"dataservices/commons/eventbus"
	"dataservices/commons/logger"
	"dataservices/commons/metrics"
	"dataservices/dbhubsdk"
	"dataservices/ddb"
	service "dataservices/internal/service/central"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"github.com/tidbcloud/tiinf/monitor/titracing"
	"github.com/tidbcloud/tiinf/transport/http/tigin"
	"go.uber.org/zap"
)

const (
	componentName     = "dataservice-central"
	defaultConfigPath = "/etc/dbaas/config.yaml"
)

// @title DataAPI API
// @version 1.0
// @description This is Data API server

// @host 127.0.0.1:8080
// @BasePath /api/v1/dataservices/central
func main() {
	var configPath string
	flag.StringVar(&configPath, "config-path", defaultConfigPath, "DBaaS configuration file path")
	flag.Parse()
	ctx := context.Background()

	err := sentry.SetupLoggerAndSentry(config.Global().SentryConf.DSN, config.Global().SentryConf.Environment, config.Global().SentryConf.LogLevel)
	if err != nil {
		panic(fmt.Sprintf("Initialize logger and sentry failed: %+v", err))
	}
	tilog.Info(context.Background(), fmt.Sprintf("Starting %s service", componentName),
		zap.String("gitHash", version.GitHash),
		zap.String("logLevel", config.Global().ServerConf.LogLevel))

	// Load config
	err = config.InitConfig(configPath)
	if err != nil {
		// TODO: unexpected error like this, should we throw a alert/page, just log seems not enough ?
		tilog.Fatal(ctx, "Initialize configuration failed", zap.Error(err))
	}

	setup(ctx)

	// Connect database
	config := config.Global()
	tilog.Info(context.Background(), "service config", logger.Redact("config", config), zap.String("env", config.CurrentEnv()))
	sqlDB := database.Connect(ctx,
		config.MysqlConf.User,
		config.MysqlConf.Password,
		config.MysqlConf.Host,
		config.MysqlConf.Port,
		config.MysqlConf.Database,
		config.MysqlConf.CharSet,
		config.MysqlConf.TimeZone,
	)

	eventbusStartup(ctx, sqlDB)

	apikeygenerator.InitEncrypter(
		config.DataApiKeyConf.SecretKey,
		config.DataApiKeyConf.SecretIv,
	)

	metrics.RegisterCentralMetrics()

	if !config.ServerConf.LocalMode {
		if err := dbhubsdk.InitSdk(
			config.DBHubSdkConf.KubeConfigPath,
			config.DBHubSdkConf.GardenerProjectName,
			config.DBHubSdkConf.InfraAPIConfigPath); err != nil {
			log.Fatal("Initialize dbhubsdk failed", zap.Error(err))
		}

		if err := ddb.Init(ctx, config); err != nil {
			log.Fatal("Initialize dynamodb client failed", zap.Error(err))
		}
	}

	err = redisutil.NewRedisClient(ctx, config)
	if err != nil {
		tilog.Fatal(ctx, "Initialize redis client failed.", zap.Error(err))
	}

	startup(ctx, config)
}

func startup(ctx context.Context, centralConfig *config.CentralConfig) {
	serverConfig := centralConfig.ServerConf

	/* metrics */
	mEng := tigin.Default()
	mEng.Any("/metrics", prometheusHandler())
	go mEng.Run("0.0.0.0:" + config.Global().ServerConf.MetricsPort)

	ginEng := tigin.Default()
	api.RouteCentralAPI(ginEng)
	addr := ":" + serverConfig.HttpPort
	tilog.Info(ctx, "serving HTTP Service", zap.String("address", addr))
	panic(fmt.Sprintf("HTTP Service died %+v", ginEng.Run(addr)))
}

func setup(ctx context.Context) {
	// jaeger
	if _, err := titracing.InitJaeger(componentName); err != nil {
		tilog.Error(ctx, "init tiinf jaeger provider failed", zap.Error(err))
		panic(fmt.Sprintf("failed to init tracing, err=%v", err))
	}
}

func prometheusHandler() gin.HandlerFunc {
	h := promhttp.Handler()

	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

func eventbusStartup(ctx context.Context, db *sql.DB) {
	if config.IsLocalMode() {
		return
	}
	bus := eventbus.Init("dataservice_central", db)
	service.RegisterEventbusHandler(bus)
	if err := bus.Run(); err != nil {
		tilog.Fatal(ctx, "Eventbus run failed", zap.Error(err))
	}
}
