package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"sync"

	"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
	"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)

const (
	// ErrorReturnCode returns when application crashed at initialization stage.
	ErrorReturnCode = 1

	// SuccessReturnCode returns when application closed without panic.
	SuccessReturnCode = 0

	EnvPrefix = "FROSTFS_IR"
)

var (
	wg         = new(sync.WaitGroup)
	intErr     = make(chan error) // internal inner ring errors
	logPrm     = new(logger.Prm)
	innerRing  *innerring.Server
	pprofCmp   *pprofComponent
	metricsCmp *httpComponent
	log        *logger.Logger
	cfg        *viper.Viper
	configFile *string
	configDir  *string
)

func exitErr(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(ErrorReturnCode)
	}
}

func main() {
	configFile = flag.String("config", "", "path to config")
	configDir = flag.String("config-dir", "", "path to config directory")
	versionFlag := flag.Bool("version", false, "frostfs-ir node version")
	flag.Parse()

	if *versionFlag {
		fmt.Print(misc.BuildInfo("FrostFS Inner Ring node"))

		os.Exit(SuccessReturnCode)
	}

	var err error
	cfg, err = newConfig()
	exitErr(err)

	logPrm.MetricsNamespace = "frostfs_ir"
	err = logPrm.SetLevelString(
		cfg.GetString("logger.level"),
	)
	exitErr(err)

	log, err = logger.NewLogger(logPrm)
	exitErr(err)

	ctx, cancel := context.WithCancel(context.Background())

	pprofCmp = newPprofComponent()
	pprofCmp.init()

	metricsCmp = newMetricsComponent()
	metricsCmp.init()

	innerRing, err = innerring.New(ctx, log, cfg, intErr)
	exitErr(err)

	pprofCmp.start()
	metricsCmp.start()

	// start inner ring
	err = innerRing.Start(ctx, intErr)
	exitErr(err)

	log.Info(logs.CommonApplicationStarted,
		zap.String("version", misc.Version))

	watchForSignal(cancel)

	<-ctx.Done() // graceful shutdown
	log.Debug(logs.FrostFSNodeWaitingForAllProcessesToStop)
	wg.Wait()

	log.Info(logs.FrostFSIRApplicationStopped)
}

func shutdown() {
	innerRing.Stop()
	if err := metricsCmp.shutdown(); err != nil {
		log.Debug(logs.FrostFSIRCouldNotShutdownHTTPServer,
			zap.String("error", err.Error()),
		)
	}
	if err := pprofCmp.shutdown(); err != nil {
		log.Debug(logs.FrostFSIRCouldNotShutdownHTTPServer,
			zap.String("error", err.Error()),
		)
	}
}