package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/labstack/echo-contrib/pprof"
	c "github.com/pingcap/test-infra/services/rms/config"
	controllermanager "github.com/pingcap/test-infra/services/rms/controller-manager"
	"github.com/pingcap/test-infra/services/rms/handler"
	. "github.com/pingcap/test-infra/services/rms/log"
	"github.com/pingcap/test-infra/services/rms/service/resource"
)

var (
	config = flag.String("config", "", "specify the config path")
	port   = flag.Int("port", 80, "port to listen")
)

func parseConfig(configFile string) {
	content, err := ioutil.ReadFile(configFile)
	if err != nil {
		panic(fmt.Sprintf("read config file error: %s", err))
	}

	err = c.ParseConfig(content)
	if err != nil {
		panic(fmt.Sprintf("parse config: %s", err))
	}
}

func main() {
	flag.Parse()
	parseConfig(*config)

	service := resource.NewDefaultService()
	statisticsService := resource.NewDefaultStatisticsService()
	controller := controllermanager.NewDefaultManager()

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

	go testbedReclaimer(ctx, service)
	go func() {
		if err := controller.Start(ctx); err != nil {
			// fail fast when controller error
			panic(fmt.Sprintf("controller start error: %v", err))
		}
	}()

	r := handler.NewHTTPServer(handler.NewHandler(service, statisticsService))
	pprof.Register(r)
	go func() {
		if err := r.Start(fmt.Sprintf(":%d", *port)); err != nil && err != http.ErrServerClosed {
			Log.Infof("shutting down the server, err=%v", err)
		}
	}()

	shutdown := make(chan os.Signal, 1)
	signal.Notify(shutdown, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	sig := <-shutdown
	Log.Infof("got signal [%v] to exit", sig)
	cancel()

	select {
	case sig := <-shutdown:
		// send signal again, return directly
		Log.Infof("got signal [%v] again to exit", sig)
		os.Exit(1)
	case <-time.After(10 * time.Second):
		Log.Infof("wait 10s for closed")
		break
	}
	ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := r.Shutdown(ctx); err != nil {
		Log.Fatalf(err.Error())
	}
}

func testbedReclaimer(ctx context.Context, service resource.ServiceInterface) {
	d, _ := time.ParseDuration(c.DefaultRMSConfiguration.ReclaimConfiguration.Interval)
	ticker := time.NewTicker(d)
	Log.Infof("starting to reclaim expire testbeds on the background, interval: %s", d.String())
	for {
		select {
		case <-ctx.Done():
			Log.Infof("the context has been ended, job exits")
			return
		case <-ticker.C:
			if err := service.ReclaimTestbed(); err != nil {
				Log.Errorf(err, "reclaim testbed error")
			}
		}
	}
}
