package main

import (
	"fmt"
	"github.com/spf13/cobra"
	"log"
	"os"
	"os/signal"
	"syscall"
	_ "unsafe"
	"x-ui/config"
	"x-ui/database"
	"x-ui/logger"
	"x-ui/v2ui"
	"x-ui/web"
	"x-ui/web/global"
	"x-ui/web/service"

	"github.com/op/go-logging"
)

func runWebServer() {
	log.Printf("%v %v", config.GetName(), config.GetVersion())

	switch config.GetLogLevel() {
	case config.Debug:
		logger.InitLogger(logging.DEBUG)
	case config.Info:
		logger.InitLogger(logging.INFO)
	case config.Warn:
		logger.InitLogger(logging.WARNING)
	case config.Error:
		logger.InitLogger(logging.ERROR)
	default:
		log.Fatal("unknown log level:", config.GetLogLevel())
	}

	err := database.InitDB(config.GetDBPath())
	if err != nil {
		log.Fatal(err)
	}

	var server *web.Server

	server = web.NewServer()
	global.SetWebServer(server)
	err = server.Start()
	if err != nil {
		log.Println(err)
		return
	}

	sigCh := make(chan os.Signal, 1)
	// Trap shutdown signals
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM)
	for {
		sig := <-sigCh

		switch sig {
		case syscall.SIGHUP:
			err := server.Stop()
			if err != nil {
				logger.Warning("stop server err:", err)
			}
			server = web.NewServer()
			global.SetWebServer(server)
			err = server.Start()
			if err != nil {
				log.Println(err)
				return
			}
		default:
			err := server.Stop()
			if err != nil {
				return
			}
			return
		}
	}
}

func resetSetting() {
	err := database.InitDB(config.GetDBPath())
	if err != nil {
		fmt.Println(err)
		return
	}

	settingService := service.SettingService{}
	err = settingService.ResetSettings()
	if err != nil {
		fmt.Println("reset setting failed:", err)
	} else {
		fmt.Println("reset setting success")
	}
}

func showSetting(show bool) {
	if show {
		settingService := service.SettingService{}
		port, err := settingService.GetPort()
		if err != nil {
			fmt.Println("get current port failed,error info:", err)
		}
		userService := service.UserService{}
		userModel, err := userService.GetFirstUser()
		if err != nil {
			fmt.Println("get current user info failed,error info:", err)
		}
		username := userModel.Username
		userpasswd := userModel.Password
		if (username == "") || (userpasswd == "") {
			fmt.Println("current username or password is empty")
		}
		fmt.Println("current panel settings as follows:")
		fmt.Println("username:", username)
		fmt.Println("userpasswd:", userpasswd)
		fmt.Println("port:", port)
	}
}

func updateTgbotEnableSts(status bool) {
	settingService := service.SettingService{}
	currentTgSts, err := settingService.GetTgbotenabled()
	if err != nil {
		fmt.Println(err)
		return
	}
	logger.Infof("current enabletgbot status[%v],need update to status[%v]", currentTgSts, status)
	if currentTgSts != status {
		err := settingService.SetTgbotenabled(status)
		if err != nil {
			fmt.Println(err)
			return
		} else {
			logger.Infof("SetTgbotenabled[%v] success", status)
		}
	}
	return
}

func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) {
	err := database.InitDB(config.GetDBPath())
	if err != nil {
		fmt.Println(err)
		return
	}

	settingService := service.SettingService{}

	if tgBotToken != "" {
		err := settingService.SetTgBotToken(tgBotToken)
		if err != nil {
			fmt.Println(err)
			return
		} else {
			logger.Info("updateTgbotSetting tgBotToken success")
		}
	}

	if tgBotRuntime != "" {
		err := settingService.SetTgbotRuntime(tgBotRuntime)
		if err != nil {
			fmt.Println(err)
			return
		} else {
			logger.Infof("updateTgbotSetting tgBotRuntime[%s] success", tgBotRuntime)
		}
	}

	if tgBotChatid != "" {
		err := settingService.SetTgBotChatId(tgBotChatid)
		if err != nil {
			fmt.Println(err)
			return
		} else {
			logger.Info("updateTgbotSetting tgBotChatid success")
		}
	}
}

func updateSetting(port int, username string, password string) {
	err := database.InitDB(config.GetDBPath())
	if err != nil {
		fmt.Println(err)
		return
	}

	settingService := service.SettingService{}

	if port > 0 {
		err := settingService.SetPort(port)
		if err != nil {
			fmt.Println("set port failed:", err)
		} else {
			fmt.Printf("set port %v success", port)
		}
	}
	if username != "" || password != "" {
		userService := service.UserService{}
		err := userService.UpdateFirstUser(username, password)
		if err != nil {
			fmt.Println("set username and password failed:", err)
		} else {
			fmt.Println("set username and password success")
		}
	}
}

func migrateDb() {
	inboundService := service.InboundService{}

	err := database.InitDB(config.GetDBPath())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Start migrating database...")
	inboundService.MigrationRequirements()
	inboundService.RemoveOrphanedTraffics()
	fmt.Println("Migration done!")
}

func removeSecret() {
	err := database.InitDB(config.GetDBPath())
	if err != nil {
		fmt.Println(err)
		return
	}
	userService := service.UserService{}
	err = userService.RemoveUserSecret()
	if err != nil {
		fmt.Println(err)
	}
	settingService := service.SettingService{}
	err = settingService.SetSecretStatus(false)
	if err != nil {
		fmt.Println(err)
	}
}

func main() {
	var rootCmd = &cobra.Command{
		Use: "x-ui",
	}

	var runCmd = &cobra.Command{
		Use:   "run",
		Short: "Run the web server",
		Run: func(cmd *cobra.Command, args []string) {
			runWebServer()
		},
	}

	var migrateCmd = &cobra.Command{
		Use:   "migrate",
		Short: "Migrate from other/old x-ui",
		Run: func(cmd *cobra.Command, args []string) {
			migrateDb()
		},
	}

	var v2uiCmd = &cobra.Command{
		Use:   "v2-ui",
		Short: "Migrate from v2-ui",
		Run: func(cmd *cobra.Command, args []string) {
			dbPath, _ := cmd.Flags().GetString("db")
			err := v2ui.MigrateFromV2UI(dbPath)
			if err != nil {
				fmt.Println("migrate from v2-ui failed:", err)
			}
		},
	}

	v2uiCmd.Flags().String("db", fmt.Sprintf("%s/v2-ui.db", config.GetDBFolderPath()), "set v2-ui db file path")

	var settingCmd = &cobra.Command{
		Use:   "setting",
		Short: "Set settings",
	}

	var resetCmd = &cobra.Command{
		Use:   "reset",
		Short: "Reset all settings",
		Run: func(cmd *cobra.Command, args []string) {
			resetSetting()
		},
	}

	var showCmd = &cobra.Command{
		Use:   "show",
		Short: "Show current settings",
		Run: func(cmd *cobra.Command, args []string) {
			showSetting(true)
		},
	}

	var updateCmd = &cobra.Command{
		Use:   "update",
		Short: "Update settings",
		Run: func(cmd *cobra.Command, args []string) {
			port, _ := cmd.Flags().GetInt("port")
			username, _ := cmd.Flags().GetString("username")
			password, _ := cmd.Flags().GetString("password")
			updateSetting(port, username, password)
		},
	}

	updateCmd.Flags().Int("port", 0, "set panel port")
	updateCmd.Flags().String("username", "", "set login username")
	updateCmd.Flags().String("password", "", "set login password")

	var tgbotCmd = &cobra.Command{
		Use:   "tgbot",
		Short: "Update telegram bot settings",
		Run: func(cmd *cobra.Command, args []string) {
			tgbottoken, _ := cmd.Flags().GetString("tgbottoken")
			tgbotchatid, _ := cmd.Flags().GetString("tgbotchatid")
			tgbotRuntime, _ := cmd.Flags().GetString("tgbotRuntime")
			enabletgbot, _ := cmd.Flags().GetBool("enabletgbot")
			remove_secret, _ := cmd.Flags().GetBool("remove_secret")

			if tgbottoken != "" || tgbotchatid != "" || tgbotRuntime != "" {
				updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
			}

			if remove_secret {
				removeSecret()
			}

			if enabletgbot {
				updateTgbotEnableSts(enabletgbot)
			}
		},
	}

	tgbotCmd.Flags().String("tgbottoken", "", "set telegram bot token")
	tgbotCmd.Flags().String("tgbotchatid", "", "set telegram bot chat id")
	tgbotCmd.Flags().String("tgbotRuntime", "", "set telegram bot cron time")
	tgbotCmd.Flags().Bool("enabletgbot", false, "enable telegram bot notify")
	tgbotCmd.Flags().Bool("remove_secret", false, "remove secret")

	settingCmd.AddCommand(resetCmd, showCmd, updateCmd, tgbotCmd)

	rootCmd.AddCommand(runCmd, migrateCmd, v2uiCmd, settingCmd)

	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}