package config

import (
	"bufio"
	"fmt"
	"os"
	"reflect"
	"strings"

	"be.ems/lib/global"
	"be.ems/lib/log"

	"gopkg.in/yaml.v3"
	//"github.com/go-yaml-comment/yaml"
	//"github.com/goccy/go-yaml"
)

// Yaml struct of config
type YamlConfig struct {
	Logger struct {
		File     string `yaml:"file"`
		Level    string `yaml:"level"`
		Duration int    `yaml:"duration"`
		Count    int    `yaml:"count"`
	} `yaml:"logger"`

	Pprof struct {
		Enabled bool   `yaml:"enabled"`
		Addr    string `yaml:"addr"`
	} `yaml:"pprof"`

	// Rest []struct {
	// 	IPv4           string `yaml:"ipv4"`
	// 	IPv6           string `yaml:"ipv6"`
	// 	Port           uint16 `yaml:"port"`
	// 	Scheme         string `yaml:"scheme"`
	// 	ClientAuthType int    `yaml:"clientAuthType"`
	// 	CaFile         string `yaml:"caFile"`
	// 	CertFile       string `yaml:"certFile"`
	// 	KeyFile        string `yaml:"keyFile"`
	// } `yaml:"rest"`

	Rest []RestParam

	WebServer struct {
		Enabled bool   `yaml:"enabled"`
		RootDir string `yaml:"rootDir"`
		Listen  []struct {
			Addr           string `yaml:"addr"`
			Scheme         string `yaml:"scheme"`
			ClientAuthType int    `yaml:"clientAuthType"`
			CaFile         string `yaml:"caFile"`
			CertFile       string `yaml:"certFile"`
			KeyFile        string `yaml:"keyFile"`
		} `yaml:"listen"`
	} `yaml:"webServer"`

	Database DbConfig `yaml:"database"`

	OMC struct {
		UriPrefix        string `yaml:"uriPrefix"`
		NeType           string `yaml:"neType"`
		NeId             string `yaml:"neId"`
		RmUID            string `yaml:"rmUID"`
		NeName           string `yaml:"neName"`
		Province         string `yaml:"province"`
		Vendor           string `yaml:"vendor"`
		Dn               string `yaml:"dn"`
		Chk2Ne           bool   `yaml:"chk2ne"`
		Sn               string `yaml:"sn"`
		CheckSign        bool   `yaml:"checksign"`
		RootDir          string `yaml:"rootDir"`
		BinDir           string `yaml:"binDir"`
		Backup           string `yaml:"backup"`
		Upload           string `yaml:"upload"`
		FrontUpload      string `yaml:"frontUpload"`
		FrontTraceDir    string `yaml:"frontTraceDir"`
		Software         string `yaml:"software"`
		License          string `yaml:"license"`
		GtpUri           string `yaml:"gtpUri"`
		CheckContentType bool   `yaml:"checkContentType"`
		TestMode         bool   `yaml:"testMode"`
		RBACMode         bool   `yaml:"rbacMode"`
		RunDir           string `yaml:"runDir"`
		CmdTimeout       int    `yaml:"cmdTimeout"`
	} `yaml:"omc"`

	Alarm AlarmConfig `yaml:"alarm"`

	MML MMLParam `yaml:"mml"`

	NE struct {
		Addr           string `yaml:"addr"`
		Port           uint16 `yaml:"port"`
		User           string `yaml:"user"`
		EtcDir         string `yaml:"etcdir"`
		BinDir         string `yaml:"bindir"`
		OmcDir         string `yaml:"omcdir"`
		ScpDir         string `yaml:"scpdir"`
		LicenseDir     string `yaml:"licensedir"`
		EtcListIMS     string `yaml:"etcListIMS"`
		EtcListDefault string `yaml:"etcListDefault"`
		DpkgOverwrite  bool   `yaml:"dpkgOverwrite"`
		DpkgTimeout    int    `yaml:"dpkgTimeout"`
	} `yaml:"ne"`

	Auth struct {
		Crypt      string `yaml:"crypt"`
		Token      bool   `yaml:"token"`
		Expires    uint32 `yaml:"expires"`
		Session    string `yaml:"session"`
		PublicKey  string `yaml:"publicKey"`
		PrivateKey string `yaml:"privateKey"`
	} `yaml:"auth"`

	Params struct {
		RmUIDMaxNum   int    `yaml:"rmuidmaxnum"`
		AlarmIDMaxNum int    `yaml:"alarmidmaxnum"`
		PmIDMaxNum    int    `yaml:"pmidmaxnum"`
		SubIDMaxNum   int    `yaml:"subidmaxnum"`
		UriMaxLen     int    `yaml:"urimaxlen"`
		RmUIDRegexp   string `yaml:"rmuidregexp"`
	} `yaml:"params"`

	TestConfig struct {
		Enabled bool   `yaml:"enabled"`
		File    string `yaml:"file"`
	} `yaml:"testConfig"`
}

type RestParam struct {
	IPv4           string `yaml:"ipv4"`
	IPv6           string `yaml:"ipv6"`
	Port           uint16 `yaml:"port"`
	Scheme         string `yaml:"scheme,omitempty" default:"http"`
	ClientAuthType int    `yaml:"clientAuthType"`
	CaFile         string `yaml:"caFile"`
	CertFile       string `yaml:"certFile"`
	KeyFile        string `yaml:"keyFile"`
}

type DbConfig struct {
	Type      string `yaml:"type"`
	User      string `yaml:"user"`
	Password  string `yaml:"password"`
	Host      string `yaml:"host"`
	Port      string `yaml:"port"`
	Name      string `yaml:"name"`
	ConnParam string `yaml:"connParam,omitempty"`
	Backup    string `yaml:"backup"`
}

type AlarmConfig struct {
	SplitEventAlarm bool `yaml:"splitEventAlarm"`
	//ForwardAlarm    bool `yaml:"forwardAlarm"`

	EmailForward struct {
		Enable        bool   `yaml:"enable" json:"enable"`
		EmailList     string `yaml:"emailList" json:"emailList"`
		SMTP          string `yaml:"smtp" json:"smtp"`
		Port          uint16 `yaml:"port" json:"port"`
		User          string `yaml:"user" json:"user"`
		Password      string `yaml:"password" json:"password"`
		TLSSkipVerify bool   `yaml:"tlsSkipVerify" json:"tlsSkipVerify"`
	} `yaml:"alarmEmailForward"`
	SMSCForward struct {
		Enable        bool   `yaml:"enable" json:"enable"`
		MobileList    string `yaml:"mobileList" json:"mobileList"`
		SMSCAddr      string `yaml:"smscAddr" json:"smscAddr"`
		SystemID      string `yaml:"systemID" json:"systemID"`
		Password      string `yaml:"password" json:"password"`
		SystemType    string `yaml:"systemType" json:"systemType"`
		DataCoding    byte   `yaml:"dataCoding" json:"dataCoding"`
		ServiceNumber string `yaml:"serviceNumber" json:"serviceNumber"`
	} `yaml:"alarmSMSForward"`
	SMS struct {
		ApiURL          string `yaml:"apiURL"`
		AccessKeyID     string `yaml:"AccessKeyID"`
		AccessKeySecret string `yaml:"accessKeySecret"`
		SignName        string `yaml:"signName"`
		TemplateCode    string `yaml:"templateCode"`
	} `yaml:"smsForward"`
	SMProxy string `yaml:"smProxy"`
}

type MMLParam struct {
	Sleep      int64  `yaml:"sleep"`
	DeadLine   int64  `yaml:"deadLine"`
	SizeRow    int16  `yaml:"sizeRow"`
	SizeCol    int16  `yaml:"sizeCol"`
	BufferSize int    `yaml:"bufferSize"`
	MmlHome    string `yaml:"mmlHome"`
}

type TestDatas struct {
	UDM struct {
		CapUsed        uint32   `yaml:"capUsed"`
		FeatureEnabled []string `yaml:"featureEnabled"`
	} `yaml:"udm"`
	AUSF struct {
		CapUsed        uint32   `yaml:"capUsed"`
		FeatureEnabled []string `yaml:"featureEnabled"`
	} `yaml:"ausf"`
	AMF struct {
		CapUsed        uint32   `yaml:"capUsed"`
		FeatureEnabled []string `yaml:"featureEnabled"`
	} `yaml:"amf"`
	SMF struct {
		CapUsed        uint32   `yaml:"capUsed"`
		FeatureEnabled []string `yaml:"featureEnabled"`
	} `yaml:"smf"`
	UPF struct {
		CapUsed        uint32   `yaml:"capUsed"`
		FeatureEnabled []string `yaml:"featureEnabled"`
	} `yaml:"upf"`
}

type NeTestData struct {
	CapUsed        uint32   `yaml:"capUsed"`
	FeatureEnabled []string `yaml:"featureEnabled"`
}
type TestDataMap struct {
	NeTestDatas []map[string]NeTestData
}

var yamlConfig YamlConfig = NewYamlConfig()

type YamlConfigFile struct {
	FilePath     string     `json:"filePath"`
	ConfigLines  YamlConfig `json:"configLines"`
	OrignalLines []string   `json:"orignalLines"`
}

var YamlConfigInfo YamlConfigFile = YamlConfigFile{
	ConfigLines: NewYamlConfig(),
}

// set default value for yaml config
func NewYamlConfig() YamlConfig {
	return YamlConfig{
		Database: DbConfig{
			Type:      "mysql",
			ConnParam: "charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&interpolateParams=True",
		},
		MML: MMLParam{
			SizeRow:    200,
			SizeCol:    120,
			BufferSize: 65535,
		},
		Alarm: AlarmConfig{
			SplitEventAlarm: true,
		},
	}
}

func ReadConfig(configFile string) {
	YamlConfigInfo.FilePath = configFile

	yamlFile, err := os.ReadFile(configFile)
	if err != nil {
		fmt.Println("Read yaml config file error:", err)
		os.Exit(2)
	}
	//	fmt.Println("yamlfile:", string(yamlFile))

	err = yaml.Unmarshal(yamlFile, &YamlConfigInfo.ConfigLines)
	if err != nil {
		fmt.Println("Unmarshal error:", err)
		os.Exit(3)
	}
	yamlConfig = YamlConfigInfo.ConfigLines

	ReadOriginalConfig(configFile)
}

func ReadOriginalConfig(configFile string) {
	// 读取原始YAML文件
	inputFile, err := os.Open(configFile)
	if err != nil {
		fmt.Println("failed to open:", err)
		os.Exit(3)
	}
	defer inputFile.Close()

	scanner := bufio.NewScanner(inputFile)
	for scanner.Scan() {
		YamlConfigInfo.OrignalLines = append(YamlConfigInfo.OrignalLines, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		fmt.Println("failed to scanner:", err)
		os.Exit(3)
	}
}

func WriteOrignalConfig(configFile string, paramName string, paramData map[string]any) error {
	lines := YamlConfigInfo.OrignalLines
	for i, line := range lines {
		if strings.Contains(line, paramName) {
			for k, v := range paramData {
				// find the first line nearby the paramName
				for j := i + 1; j < len(lines); j++ {
					if strings.Contains(lines[j], k+":") {
						index := strings.Index(lines[j], k)
						// Determine the type of v
						switch v := v.(type) {
						case string:
							lines[j] = lines[j][:index] + fmt.Sprintf("%s: \"%s\"", k, v)
						// case int:
						// 	lines[j] = lines[j][:index] + fmt.Sprintf("%s: %d", k, v)
						// case float64:
						// 	lines[j] = lines[j][:index] + fmt.Sprintf("%s: %f", k, v)
						case bool:
							lines[j] = lines[j][:index] + fmt.Sprintf("%s: %t", k, v)
						default:
							lines[j] = lines[j][:index] + fmt.Sprintf("%s: %v", k, v)
						}
						break
					}
				}
			}
			break
		}
	}

	// write back to yaml file
	outputFile, err := os.Create(configFile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	defer outputFile.Close()

	writer := bufio.NewWriter(outputFile)
	for _, line := range YamlConfigInfo.OrignalLines {
		writer.WriteString(line + "\n")
	}
	writer.Flush()
	return nil
}

func WriteYamlConfig(newConfigData YamlConfig, configFile string) error {
	// 将配置转换回YAML数据
	newYamlData, err := yaml.Marshal(&newConfigData)
	if err != nil {
		log.Errorf("Failed to marshal YAML: %v", err)
		return err
	}

	// 将新的YAML数据写入文件
	err = os.WriteFile(configFile, newYamlData, 0644)
	if err != nil {
		log.Errorf("Failed to write YAML file: %v", err)
		return err
	}
	return nil
}

var mapYaml map[string]interface{}

func ReadParamConfig(fileName string) *map[string]interface{} {
	file, err := os.ReadFile(fileName)
	if err != nil {
		fmt.Println("Read yaml file error:", err)
	}

	mapYaml = make(map[string]interface{})

	err = yaml.Unmarshal(file, &mapYaml)
	if err != nil {
		fmt.Printf("yaml.Unmarshal: %v when to struct", err)
	}
	//	fmt.Println("mapYaml:", mapYaml)

	return &mapYaml
}

func UpdateStructFromMap(s any, updates map[string]any) {
	v := reflect.ValueOf(s).Elem()
	t := v.Type()

	for key, value := range updates {
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			if field.Tag.Get("json") == key {
				structField := v.FieldByName(field.Name)
				if structField.IsValid() && structField.CanSet() {
					// Convert value to the appropriate type if necessary
					convertedValue := reflect.ValueOf(value).Convert(structField.Type())
					if structField.Type() == convertedValue.Type() {
						structField.Set(convertedValue)
					}
				}
				break
			}
		}
	}
}

func GetYamlConfig() *YamlConfig {
	return &YamlConfigInfo.ConfigLines
}

func GetAuthFromConfig() interface{} {
	return yamlConfig.Auth
}

func GetExpiresFromConfig() uint32 {
	return yamlConfig.Auth.Expires
}

func GetRmUIDFromConfig() string {
	return yamlConfig.OMC.RmUID
}

func GetRmUIDRegexpFromConfig() string {
	return yamlConfig.Params.RmUIDRegexp
}

func GetRmUIDMaxNumFromConfig() int {
	return yamlConfig.Params.RmUIDMaxNum
}

func GetAlarmIDMaxNumFromConfig() int {
	return yamlConfig.Params.AlarmIDMaxNum
}

func GetPmIDMaxNumFromConfig() int {
	return yamlConfig.Params.PmIDMaxNum
}

func GetSubIDMaxNumFromConfig() int {
	return yamlConfig.Params.SubIDMaxNum
}

func GetUriMaxLenFromConfig() int {
	return yamlConfig.Params.UriMaxLen
}

func GetLogLevel() log.LogLevel {
	var logLevel log.LogLevel
	switch strings.ToLower(yamlConfig.Logger.Level) {
	case "trace":
		logLevel = log.LOG_TRACE
	case "info":
		logLevel = log.LOG_INFO
	case "debug":
		logLevel = log.LOG_DEBUG
	case "warn":
		logLevel = log.LOG_WARN
	case "error":
		logLevel = log.LOG_ERROR
	case "fatal":
		logLevel = log.LOG_FATAL
	case "off":
		logLevel = log.LOG_OFF
	default:
		logLevel = log.LOG_DEBUG
	}
	return logLevel
}

var (
	DefaultUriPrefix string = "/api/rest"
	UriPrefix        string = "/omc/rest"
	//TestDataUDM  []map[string]interface{}
	TDatas map[string]NeTestData
)

func ReadTestConfigYaml(pfile string) (ret error) {
	file, err := os.ReadFile(pfile)
	if err != nil {
		return err
	}

	err = yaml.Unmarshal(file, &TDatas)
	if err != nil {
		fmt.Println("Failed to Unmarshal:", err)
		return err
	}

	return nil
}

func GetDefaultUserAgent() string {
	return "OMC-restagent/" + global.Version
}

func GetOMCHostUrl() string {
	var omcip string = "127.0.0.1"
	var rest RestParam = yamlConfig.Rest[0]
	var port uint16 = rest.Port
	if rest.IPv4 != "0.0.0.0" && rest.IPv4 != "" {
		omcip = rest.IPv4
	} else if rest.IPv6 != "::" && rest.IPv6 != "" {
		omcip = "[" + rest.IPv6 + "]"
	}
	var scheme string = "http"
	if rest.Scheme != "" {
		scheme = rest.Scheme
	}

	return fmt.Sprintf("%s://%s:%d", scheme, omcip, port)
}

// const defaultConfigFile = "./etc/restconf.yaml"

// func init() {
// 	cfile := flag.String("c", defaultConfigFile, "config file")
// 	pv := flag.Bool("version", false, "print version")
// 	ph := flag.Bool("help", false, "print help")

// 	//global.BuildTime = "Wed May 31 18:24:04 CST 2023"
// 	//global.GoVer = "go version go1.15.7 linux/arm64"
// 	flag.Parse()
// 	if *pv {
// 		fmt.Printf("OMC restagent version: %s\n%s\n%s\n\n", global.Version, global.BuildTime, global.GoVer)
// 		os.Exit(0)
// 	}
// 	if *ph {
// 		flag.Usage()
// 		os.Exit(0)
// 	}

// 	// 使用viper读取配置
// 	conf.InitConfig(*cfile)

// 	ReadConfig(*cfile)
// 	if GetYamlConfig().OMC.UriPrefix != "" {
// 		UriPrefix = GetYamlConfig().OMC.UriPrefix
// 	}
// 	if GetYamlConfig().TestConfig.Enabled {
// 		ReadTestConfigYaml(GetYamlConfig().TestConfig.File)
// 	}
// }
