package main

import (
	"context"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"../log"
	"../nt"
	"../prof"
	pb "../protos"
	"../utils"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

const (
	KEY = "coaGBD8Iq5v71dTK" // use for secure communication with PHP
)

var (
	FileConf     string                              //　配置文件文件名
	ChQuit       chan struct{} = make(chan struct{}) // 进程退出通知
	Ev                         = new(MgrEvent)
	ErrSignCheck               = errors.New("Sign check failed")
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// 解析参数
	var ver bool = false
	flag.StringVar(&FileConf, "c", "conf.json", "config file")
	flag.BoolVar(&ver, "v", false, "print version")
	flag.Parse()

	if ver {
		fmt.Println(VERSION)
		return
	}

	//
	init_by_conf()
	//
	profile := prof.NewProf(nt.InstConf.ProfAddr, true)
	//
	gin.SetMode(nt.InstConf.Mode)

	// 路由
	r := gin.Default()
	r.GET("/", do_homepage)
	r.POST("/mgr", do_manager)

	addr := fmt.Sprintf("%s:%d", nt.InstConf.Host, nt.InstConf.Port)
	server := &http.Server{
		Addr:    addr,
		Handler: r,
	}
	go func() {
		// 开启服务
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			fmt.Println("listen error:", err)
		}
	}()

	nt.PrintInstInfo(nt.Mac)

	// 退出方式
	chSig := make(chan os.Signal)
	signal.Notify(chSig, syscall.SIGINT, syscall.SIGTERM)

	fmt.Println("Signal:", <-chSig) // will be blocked here

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := server.Shutdown(ctx); err != nil {
		fmt.Println("Server shutdown error:", err)
	}
	select {
	case <-ctx.Done():
		fmt.Println("Timeout and quit")
	}

	profile.Stop()
	//
	close(ChQuit)

	fmt.Println("Server Exited.")
}

// 首页
func do_homepage(c *gin.Context) {
	c.String(http.StatusOK, "Welcome to %s v%s", nt.InstName, nt.VERSION)
}

// 命令
func do_manager(c *gin.Context) {
	ip := c.ClientIP()
	req := &MgrRequest{}
	s := c.Query("s")

	body, err := c.GetRawData()
	if nil != err {
		log.Warn("no body", zap.String("ip", ip), zap.Error(err))
		c.AbortWithStatus(http.StatusBadRequest)
	}

	err = json.Unmarshal(body, req)
	if err != nil {
		log.Warn("bad request", zap.String("ip", ip), zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	//TODO ip 白名单
	// 验证
	sign := utils.Md5(append(body, []byte(KEY)...))
	if sign != s {
		log.Warn("sign", zap.Error(ErrSignCheck))
		c.JSON(http.StatusOK, gin.H{
			"cmd": req.Cmd,
			"result": gin.H{
				"ec": pb.ErrCode_AUTH,
			},
		})
		return
	}
	dispatch(c, req, body)
}
