package conf

import (
	"fmt"
	log2 "log"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/stevenroose/gonfig"
	"github.com/v2rayA/v2rayA/common"
	"github.com/v2rayA/v2rayA/pkg/util/log"
)

type Params struct {
	Address              string `id:"address" short:"a" default:"0.0.0.0:2017" desc:"Listening address"`
	Socket               string `id:"socket" desc:"Unix socket path for listening"`
	BaseUrl              string `id:"baseurl" default:"/" desc:"Base URL path prefix"`
	V2rayBin             string `id:"v2ray-bin" short:"b" desc:"Executable v2ray binary path. Auto-detect if put it empty."`
	CoreType             string `id:"core-type" desc:"Specify core type: v2ray or xray. Required when using custom v2ray-bin path."`
	Config               string `id:"config" short:"c" desc:"v2rayA configuration directory"`
	V2rayConfigDirectory string `id:"v2ray-confdir" desc:"Additional v2ray config directory, files in it will be combined with config generated by v2rayA"`
	V2rayAssetsDirectory string `id:"v2ray-assetsdir" desc:"v2ray-core assets directory for searching and downloading files like geoip.dat. This will override environment V2RAY_LOCATION_ASSET and XRAY_LOCATION_ASSET."`
	CoreStartupTimeout   int64  `id:"core-startup-timeout" default:"15" desc:"Timeout duration in seconds for starting v2ray or xray core. On devices with lower performance, consider increasing this value."`
	TransparentHook      string `id:"transparent-hook" desc:"the executable file to run in the transparent proxy life-cycle. v2rayA will pass in the --transparent-type (tproxy, redirect) and --stage (pre-start, post-start, pre-stop, post-stop) arguments."`
	CoreHook             string `id:"core-hook" desc:"the executable file to run in the v2ray-core life-cycle. v2rayA will pass in the --stage (pre-start, post-start, pre-stop, post-stop) argument."`
	TinyTunBin           string `id:"tinytun-bin" desc:"Executable tinytun binary path for TUN transparent proxy mode. Auto-detect from PATH if empty."`
	PluginManager        string `id:"plugin-manager" desc:"the executable file to run in the v2ray-core life-cycle. v2rayA will pass in the --stage (pre-start, post-start, pre-stop, post-stop) argument."`
	WebDir               string `id:"webdir" desc:"v2rayA web files directory. use embedded files if not specify."`
	IPV6Support          string `id:"ipv6-support" default:"auto" desc:"Optional values: auto, on, off. Make sure your IPv6 network works fine before you turn it on."`
	NftablesSupport      string `id:"nftables-support" default:"auto" desc:"Optional values: auto, on, off. Experimental feature. Make sure you have installed nftables."`
	PassCheckRoot        bool   `id:"passcheckroot" desc:"Skip privilege checking. Use it only when you cannot start v2raya but confirm you have root privilege"`
	ResetPassword        bool   `id:"reset-password" ignore:"1"`
	LogLevel             string `id:"log-level" default:"info" desc:"Optional values: trace, debug, info, warn or error"`
	LogFile              string `id:"log-file" desc:"The path of log file"`
	LogMaxDays           int64  `id:"log-max-days" default:"3" desc:"Maximum number of days to keep log files"`
	LogDisableColor      bool   `id:"log-disable-color" ignore:"1"`
	LogDisableTimestamp  bool   `id:"log-disable-timestamp" desc:"Intended for use with systemd/journald to avoid duplicate timestamps in logs. This flag is ignored when using the --log-file flag or the V2RAYA_LOG_FILE environment variable." ignore:"1"`
	Lite                 bool   `id:"lite" desc:"Lite mode for non-root and non-linux users" ignore:"1"`
	WinEnvFile           string `id:"win-envfile" desc:"[Windows Only] Path to environment variables file to load before service starts"`
	ShowVersion          bool   `id:"version" ignore:"1"`
	PrintReport          string `id:"report" desc:"Print report" ignore:"1"`
}

var params Params

func initFunc() {
	if err := loadPlatformEnv(); err != nil {
		log.Warn("failed to load platform env: %v", err)
	}
	err := gonfig.Load(&params, gonfig.Conf{
		FileDisable:       true,
		FlagIgnoreUnknown: false,
		EnvPrefix:         "V2RAYA_",
	})
	if err != nil {
		if err.Error() != "unexpected word while parsing flags: '-test.v'" {
			log2.Fatal(err)
		}
	}
	if params.BaseUrl == "" {
		params.BaseUrl = "/"
	} else {
		if !strings.HasPrefix(params.BaseUrl, "/") {
			params.BaseUrl = "/" + params.BaseUrl
		}
		if params.BaseUrl != "/" {
			params.BaseUrl = strings.TrimSuffix(params.BaseUrl, "/")
		}
	}
	if params.ShowVersion {
		fmt.Println(Version)
		os.Exit(0)
	}
	if params.Lite {
		params.PassCheckRoot = true
	}
	if params.Config == "" {
		params.Config = defaultConfigDir(params.Lite)
	}
	// Fix mis-detected Linux-style config path on Windows
	params.Config = sanitizeConfigDirForPlatform(params.Config, params.Lite)
	// replace all dots of the filename with underlines
	params.Config = filepath.Join(
		filepath.Dir(params.Config),
		strings.ReplaceAll(filepath.Base(params.Config), ".", "_"),
	)
	// expand '~' with user home
	params.Config, err = common.HomeExpand(params.Config)
	if err != nil {
		log2.Fatal(err)
	}
	if strings.Contains(params.Config, "$HOME") {
		if h, err := os.UserHomeDir(); err == nil {
			params.Config = strings.ReplaceAll(params.Config, "$HOME", h)
		}
	}
	// 展开各路径参数中的平台相关环境变量（Windows 上处理 %VAR% 风格）
	expandPlatformConfigPaths(&params)
	if _, err := os.Stat(params.Config); os.IsNotExist(err) {
		_ = os.MkdirAll(params.Config, os.ModeDir|0750)
	} else if err != nil {
		log.Warn("%v", err)
	}
	logWay := "console"
	if params.LogFile != "" {
		logWay = "file"
		_ = os.MkdirAll(filepath.Dir(params.LogFile), os.ModeDir|0750)
	}
	log.InitLog(logWay, params.LogFile, params.LogLevel, params.LogMaxDays, params.LogDisableColor, params.LogDisableTimestamp)

	// V2rayAssetsDirectory
	if params.V2rayAssetsDirectory != "" {
		if err = os.Setenv("V2RAY_LOCATION_ASSET", params.V2rayAssetsDirectory); err != nil {
			log.Fatal("failed to set V2rayAssetsDirectory: %v", err)
		}
		if err = os.Setenv("XRAY_LOCATION_ASSET", params.V2rayAssetsDirectory); err != nil {
			log.Fatal("failed to set V2rayAssetsDirectory: %v", err)
		}
	}

	if params.CoreStartupTimeout <= 0 {
		log.Fatal("invalid CoreStartupTimeout: %d seconds. It must be a positive integer greater than 0.", params.CoreStartupTimeout)
	}
}

var once sync.Once

func GetEnvironmentConfig() *Params {
	once.Do(initFunc)
	return &params
}

func SetConfig(config Params) {
	params = config
}
