package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/spf13/cobra"

	ctrlcomm "github.com/tidbcloud/devops/pkg/controller/common"
	jmpagctrl "github.com/tidbcloud/devops/pkg/controller/jump/agent"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/infra/utils/signals"
	"github.com/tidbcloud/devops/pkg/service/jump/jumppod/agent"
)

var (
	listenAddr string
	inputDir   string
	outputDir  string
)

func newRootCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "jump-pod-agent",
		Version: "v0.0.0",
		Run:     exec,
	}

	cmd.Flags().StringVar(&listenAddr, "addr", "0.0.0.0:11277", "the http addr")
	cmd.Flags().StringVar(&inputDir, "input-dir", "/root/.jump-pod-agent", "the path to read storage") // MUST match the mount path in Pod
	cmd.Flags().StringVar(&outputDir, "output-dir", "/root/.jump", "the path to store kube cred")      // MUST match the mount path in Pod
	return cmd
}

func exec(cmd *cobra.Command, args []string) {
	ctx := signals.SetupSignalHandler()

	err := setup(ctx)
	exitIfErr(err, "failed to setup")

	err = run(ctx)
	exitIfErr(err, "failed to run")
}

func setup(ctx context.Context) error {
	cfg := &config.LogConfig{
		Level: 4,
	}
	return log.InitLogger(cfg)
}

func run(ctx context.Context) error {
	// agent
	ag := agent.NewJumpPodAgent(inputDir, outputDir, log.Logger)

	logmw := &ctrlcomm.LogMetricMiddlewareBuilder{
		Request: ctrlcomm.RequestLoggerOptions{
			Logger: log.Logger,
		},
		Access: &ctrlcomm.AccessLoggerOptions{
			Logger: log.Logger.WithName("Access"),
		},
	}

	// router
	eng := gin.New()
	eng.Use(logmw.Build())

	// controller
	ctrl := jmpagctrl.NewJumpPodAgentController(ag)
	ctrl.RegisterAPI(eng)

	// http server
	server := &http.Server{
		Addr:              listenAddr,
		Handler:           eng,
		ReadHeaderTimeout: 10 * time.Second,
	}

	// run
	err := ag.Init()
	if err != nil {
		return fmt.Errorf("failed to init agent: %w", err)
	}

	errCh := make(chan error, 1)
	go func() {
		err := server.ListenAndServe()
		errCh <- err
	}()

	select {
	case err := <-errCh:
		return fmt.Errorf("failed to run: %w", err)
	case <-ctx.Done():
		return server.Shutdown(ctx)
	}
}

func main() {
	cmd := newRootCmd()
	err := cmd.Execute()
	exitIfErr(err, "failed to execute cmd")
}

func exitIfErr(err error, msg string) {
	if err != nil {
		if log.Logger.IsZero() {
			fmt.Fprintln(os.Stderr, err.Error())
		} else {
			log.Logger.Error(err, "exit err")
		}
		os.Exit(1)
	}
}
