package main

import (
	"context"
	"encoding/json"
	"flag"
	"net/http"

	"github.com/tidbcloud/account/central/events/receive"

	"github.com/tidbcloud/account/commons/eventbus"

	"github.com/tidbcloud/account/internel/svc/auditlog"
	"github.com/tidbcloud/account/internel/svc/migrate"

	"github.com/tidbcloud/account/commons/database"

	"github.com/gin-gonic/gin"
	"github.com/mailgun/mailgun-go/v4"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sendgrid/sendgrid-go"
	"github.com/tidbcloud/account/central/jobqueue"
	"github.com/tidbcloud/account/central/jobs"
	"github.com/tidbcloud/account/central/utils"
	opworker "github.com/tidbcloud/account/central/worker"
	"github.com/tidbcloud/account/commons/config"
	_ "github.com/tidbcloud/account/internel/svc/migrate/central_migrate_record"
	gcpauth "github.com/tidbcloud/account/pkg/infra/provider/gcp/auth"
	"github.com/tidbcloud/account/pkg/version"
	mailv2 "github.com/tidbcloud/pkgv2/mail/v2"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"github.com/tidbcloud/tiinf/monitor/titracing"
	"github.com/tidbcloud/tiinf/transport/http/tigin"
	"go.uber.org/zap"
)

const (
	defaultConfigPath = "/etc/dbaas/config.yaml"
)

type versionInfo struct {
	Hash             string `json:"hash"`
	CentralDBVersion int    `json:"central_db_version"`
	TenantDBVersion  int    `json:"tenant_db_version"`
}

// versionHandler outputs the current central server version information.
func versionHandler(w http.ResponseWriter, _ *http.Request) {
	data, _ := json.Marshal(versionInfo{
		Hash:             version.GitHash,
		CentralDBVersion: migrate.GetCentralDBVersion(),
	})
	_, _ = w.Write(data)
}

func main() {
	var configPath string
	flag.StringVar(&configPath, "config-path", defaultConfigPath, "dbaas configuration file path")
	flag.Parse()
	ctx := context.Background()

	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))
	}

	err = utils.SetupLoggerAndSentry()
	if err != nil {
		tilog.Panic(ctx, "Setup logger and sentry failed", zap.Error(err))
	}

	auditlog.SetupAuditLogger()

	if !config.IsLocalEnv() {
		err = gcpauth.InitWebIdentityEnv()
		if err != nil {
			tilog.Panic(ctx, "initialize the web identity environment variables failed", zap.Error(err))
		}
		_, err = titracing.InitJaeger("account-worker")
		if err != nil {
			tilog.Panic(ctx, "create trace provider failed", zap.Error(err))
		}
	}

	// Initialize global database.
	mysqlConfig := config.Global().MysqlConfig
	tilog.Info(ctx, "Initialize global database connection", zap.String("user", mysqlConfig.User))

	migrationFlag := false
	if !config.IsLocalEnv() {
		tilog.Info(ctx, "Initialize global database connection", zap.String("user", mysqlConfig.User))
		migrationFlag = true
	}
	gormDB, err := database.Initialize(ctx, mysqlConfig)
	if err != nil {
		tilog.Fatal(ctx, "Initialize global database connection failed", zap.Error(err))
	}
	if migrationFlag {
		if err = migrate.RunCentralMigrate(gormDB); err != nil {
			if err != nil {
				tilog.Fatal(ctx, "RunCentralMigrate failed", zap.Error(err))
			}
		}
	}
	if !config.IsLocalEnv() {

		err = eventbus.Get().Run()
		if err != nil {
			tilog.Fatal(ctx, "Initialize event bus handler failed", zap.Error(err))
		}

		// init jobqueue
		if err := jobqueue.InitDefaultJobQueue(
			ctx,
			config.Global().JobQueueConfig.JobQueueURL,
			config.Global().JobQueueConfig.JobQueueDLURL,
			config.Global().JobQueueConfig.JobQueueRegion,
			config.Global().JobQueueConfig.JobQueueTimeout,
		); err != nil {
			tilog.Fatal(ctx, "Initialize jobqueue failed", zap.Error(err))
		}
		jobqueue.EnableAsync()
		if err = jobs.InitDeps(); err != nil {
			tilog.Fatal(ctx, "Initialize salesforce client failed", zap.Error(err))
		}
		jobs.RegisterHandlers()

		go func() {
			ctx := context.Background()
			if err := jobqueue.StartPolling(ctx); err != nil {
				tilog.Fatal(ctx, "Start polling jobqueue failed", zap.Error(err))
			}
		}()
	}

	serverConfig := config.Global().ServerConfig
	mg := mailgun.NewMailgun(config.Domain, serverConfig.MailGunApiKey)
	sgCli := sendgrid.NewSendClient(serverConfig.SendgridApiKey)
	mailFacade := mailv2.NewMailProvider(mg, sgCli, config.SendgridTemplateName2id)
	cs := opworker.New(mailFacade)

	tilog.Info(ctx, "start background workers.")
	if err := cs.StartBackgroundWorkers(); err != nil {
		tilog.Fatal(ctx, "Failed to start background workers", zap.Error(err))
	}

	go receive.RedisSubscribe(context.Background())
	tilog.Info(ctx, "start healthz")
	eng := tigin.Default()
	eng.Any("/healthz", gin.WrapF(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(200) }))
	eng.Any("/version", gin.WrapF(versionHandler))
	eng.Any("/metrics", gin.WrapH(promhttp.Handler()))

	panic(eng.Run("0.0.0.0:11000"))
}
