package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"virjar.com/majora-go/client"
	"virjar.com/majora-go/common"
	"virjar.com/majora-go/env"
	"virjar.com/majora-go/log"
	"virjar.com/majora-go/model"
	"virjar.com/majora-go/safe"
)

var (
	configure string
	logLevel  string
	pprofPort int
	natServer string
	account   string
	dnsServer string
	localAddr string
	daemon    bool
)

var (
	Version string
	Date    string
)

var (
	cmd *exec.Cmd
)

func init() {
	rand.Seed(time.Now().UnixNano())
	flag.StringVar(&configure, "conf", "", "./majora -c path/to/your/majora.ini")
	flag.StringVar(&logLevel, "log", "info", "log logLevel")
	flag.IntVar(&pprofPort, "pprof", 0, "enable pprof")
	flag.StringVar(&natServer, "natServer", common.DefNatAddr, "natServer")
	flag.StringVar(&account, "account", "unknown", "account")
	flag.StringVar(&dnsServer, "dnsServer", common.DNSServer, "custom dns server")
	flag.StringVar(&localAddr, "localIp", "", "bind local ip")
	flag.BoolVar(&daemon, "daemon", false, "daemon")

	flag.Parse()
}

func initial(cfg *model.Configure) {
	// 忽略错误，默认 product 环境
	_ = env.CurrentEnv.Set(cfg.Env)

	log.Init(cfg.LogLevel)
	if cfg.PprofPort > 0 {
		safe.SageGo(func() {
			addr := fmt.Sprintf("127.0.0.1:%d", cfg.PprofPort)
			log.Run().Infof("enable pprof: %s", addr)
			log.Run().Error(http.ListenAndServe(addr, nil))
		})
	}

	if len(cfg.DNSServer) > 0 {
		net.DefaultResolver = &net.Resolver{
			PreferGo: true,
			Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
				return net.Dial("udp", dnsServer)
			},
		}
	}
}

func parseFromCmd(cfg *model.Configure) {
	cfg.TunnelAddr = natServer
	cfg.LogLevel = logLevel
	cfg.DNSServer = dnsServer
	cfg.LocalAddr = localAddr
	// 先兼容吧
	cfg.Extra.Account = account
	cfg.PprofPort = pprofPort
}

func cli(cfg *model.Configure) {
	defer func() {
		if err := recover(); err != nil {
			log.Error().Errorf("cli panic %+v", err)
		}
	}()
	log.Run().Infof("cpu count %d proc %d", runtime.NumCPU(), runtime.NumCPU()*2)
	log.Run().Infof("current Version %s, build at %s", Version, Date)
	log.Run().Infof("hostInfo os:%s, arch:%s", runtime.GOOS, runtime.GOARCH)
	cfgInfo, _ := json.Marshal(cfg)
	log.Run().Infof("config info:%s", string(cfgInfo))
	client.NewClientWithConf(cfg).StartUp()
}

func initConf() *model.Configure {
	cfg := model.NewDefMajoraConf()
	if len(configure) > 0 {
		cfg = model.InitConf(configure)
	} else {
		parseFromCmd(cfg)
	}
	//runtime.GOMAXPROCS(runtime.NumCPU() * 2)
	//debug.SetGCPercent(200)
	initial(cfg)
	return cfg
}

//main start
func main() {
	if len(os.Args) > 1 && os.Args[1] == "version" {
		fmt.Println(Version)
		os.Exit(0)
	}
	cfg := initConf()
	if daemon {
		var args []string
		for _, arg := range os.Args[1:] {
			if arg != "-daemon" {
				args = append(args, arg)
			}
		}
		cmd = exec.Command(os.Args[0], args...)
		cmd.Env = os.Environ()
		if err := cmd.Start(); err != nil {
			panic(err)
		}
		log.Run().Infof("%s [pid-%d] running...\n", os.Args[0], cmd.Process.Pid)
		os.Exit(0)
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	cli(cfg)
	select {
	case <-signalChan:
		time.Sleep(time.Second * 3)
		log.Run().Warn("main process exit...")
	}
}
