package state

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/shirou/gopsutil/v3/net"

	"github.com/go-resty/resty/v2"
	"github.com/gorilla/mux"

	"be.ems/lib/dborm"
	"be.ems/lib/global"
	"be.ems/lib/log"
	"be.ems/lib/services"
	"be.ems/restagent/config"
	tokenConst "be.ems/src/framework/constants/token"
)

type CpuUsage struct {
	NfCpuUsage  uint16 `json:"nfCpuUsage"`
	SysCpuUsage uint16 `json:"sysCpuUsage"`
}

type MemUsage struct {
	TotalMem    uint32 `json:"totalMem"`
	NfUsedMem   uint32 `json:"nfUsedMem"`
	SysMemUsage uint16 `json:"sysMemUsage"`
}

type PartitionInfo struct {
	Total uint32 `json:"total"` // MB
	Used  uint32 `json:"used"`  // MB
}

type DiskSpace struct {
	PartitionNum  uint8           `json:"partitionNum"`
	PartitionInfo []PartitionInfo `json:"partitionInfo"`
}

type HardwareInfo struct {
	CPUs   int `json:"cpus"`
	Memory int `json:"memory"`
}

type SysState struct {
	HostName     string       `json:"hostName"` // linux命令: hostname
	OsInfo       string       `json:"osInfo"`   // linux命令: uname -a
	DbInfo       string       `json:"dbInfo"`   // 网元如果有db, 显示数据库名和版本信息， OMC: mysql --version
	Version      string       `json:"version"`  // 软件版本信息: 16.1.1
	IpAddr       []string     `json:"ipAddr"`   // 网管的ipv4和ipv6列表
	Port         uint16       `json:"port"`     // 用于网管的port
	Capability   uint32       `json:"capability"`
	SerialNum    string       `json:"serialNum"`
	ExpiryDate   string       `json:"expiryDate"`
	HardwareInfo HardwareInfo `json:"hardwareInfo"`
	CpuUsage     CpuUsage     `json:"cpuUsage"`
	MemUsage     MemUsage     `json:"memUsage"`
	DiskSpace    DiskSpace    `json:"diskSpace"`
	//Timestamp  string `json:"timestamp"`
}

type SystemState struct {
	HostName     string   `json:"hostName"` // linux命令: hostname
	OsInfo       string   `json:"osInfo"`   // linux命令: uname -a
	DbInfo       string   `json:"dbInfo"`   // 网元如果有db, 显示数据库名和版本信息， OMC: mysql --version
	Version      string   `json:"version"`  // 软件版本信息: 16.1.1
	IpAddr       []string `json:"ipAddr"`   // 网管的ipv4和ipv6列表
	Port         uint16   `json:"port"`     // 用于网管的port
	Capability   uint32   `json:"capability"`
	SerialNum    string   `json:"serialNum"`
	ExpiryDate   string   `json:"expiryDate"`
	HardwareInfo struct {
		CPUs   int `json:"cpus"`   // 主机(裸机/虚拟机)的cpu个数
		Memory int `json:"memory"` // 主机(裸机/虚拟机): 配置的内存
	} `json:"hardwareInfo"`
	CpuUsage struct {
		NfCpuUsage  uint16 `json:"nfCpuUsage"`
		SysCpuUsage uint16 `json:"sysCpuUsage"`
	} `json:"cpuUsage"`
	MemUsage struct {
		TotalMem    uint32 `json:"totalMem"`
		NfUsedMem   uint32 `json:"nfUsedMem"`
		SysMemUsage uint16 `json:"sysMemUsage"`
	} `json:"memUsage"`
	DiskSpace struct {
		PartitionNum  uint8 `json:"partitionNum"`
		PartitionInfo []struct {
			Total uint32 `json:"total"` // MB
			Used  uint32 `json:"used"`  // MB
		} `json:"partitionInfo"`
	} `json:"diskSpace"`
	//Timestamp  string `json:"timestamp"`
}

type SystemInfo struct {
	NeType   string `json:"neType" map:"neType, omitempty"`
	NeId     string `json:"neId" map:"neId, omitempty"`
	HostName string `json:"hostName" map:"hostName, omitempty"` // linux命令: hostname
	OsInfo   string `json:"osInfo" map:"osInfo, omitempty"`     // linux命令: uname -a
	DbInfo   string `json:"dbInfo" map:"dbInfo, omitempty"`     // 网元如果有db, 显示数据库名和版本信息， OMC: mysql --version
	Version  string `json:"version" map:"version, omitempty"`   // 软件版本信息: 16.1.1
	IpAddr   string `json:"ipAddr" map:"ipAddr, omitempty"`     // 网管的ipv4和ipv6列表
	Port     uint16 `json:"port" map:"port, omitempty"`         // 用于网管的port
	CPUs     int    `json:"cpus" map:"cpus, omitempty"`
	TotalMem int    `json:"totalMem" map:"totalMem, omitempty"`
	PvFlag   string `json:"pvFlag" map:"pvFlag, omitempty"`
	Status   string `json:"status" map:"status, omitempty"`
}

type LicenseInfo struct {
	NeType         string   `json:"neType"`
	NeId           string   `json:"neId"`
	SerialNum      string   `json:"serialNum"`
	Capability     uint32   `json:"capability"`
	CapUsed        uint32   `json:"capUsed"`
	FeatureEnabled []string `json:"featureEnabled"`
	ExpiryDate     string   `json:"expiryDate"`
}

type Response struct {
	Data interface{} `json:"data"`
}

var (
	UriSysState       = config.DefaultUriPrefix + "/systemManagement/{apiVersion}/elementType/{elementTypeValue}/objectType/systemState"
	UriSysState2      = config.DefaultUriPrefix + "/systemManagement/{apiVersion}/{elementTypeValue}/systemState"
	UriSysInfoAll     = config.DefaultUriPrefix + "/systemManagement/{apiVersion}/sysInfo"
	UriSysInfoOne     = config.DefaultUriPrefix + "/systemManagement/{apiVersion}/sysInfo/{neType}/{neId}"
	UriLicenseInfoAll = config.DefaultUriPrefix + "/systemManagement/{apiVersion}/licenseInfo"
	UriLicenseInfoOne = config.DefaultUriPrefix + "/systemManagement/{apiVersion}/licenseInfo/{neType}/{neId}"

	CustomUriSysState       = config.UriPrefix + "/systemManagement/{apiVersion}/elementType/{elementTypeValue}/objectType/systemState"
	CustomUriSysState2      = config.UriPrefix + "/systemManagement/{apiVersion}/{elementTypeValue}/systemState"
	CustomUriSysInfoAll     = config.UriPrefix + "/systemManagement/{apiVersion}/sysInfo"
	CustomUriSysInfoOne     = config.UriPrefix + "/systemManagement/{apiVersion}/sysInfo/{neType}/{neId}"
	CustomUriLicenseInfoAll = config.UriPrefix + "/systemManagement/{apiVersion}/licenseInfo"
	CustomUriLicenseInfoOne = config.UriPrefix + "/systemManagement/{apiVersion}/licenseInfo/{neType}/{neId}"
)

var client = resty.New()

func init() {
	/*
		client.
			SetTimeout(10 * time.Second).
			SetRetryCount(1).
			SetRetryWaitTime(1 * time.Second).
			SetRetryMaxWaitTime(2 * time.Second).
			SetRetryAfter(func(client *resty.Client, resp *resty.Response) (time.Duration, error) {
				return 0, errors.New("quota exceeded")
			})
	*/
	client.
		SetTimeout(time.Duration(400 * time.Millisecond))
	// SetRetryCount(1).
	// SetRetryWaitTime(time.Duration(1 * time.Second)).
	// SetRetryMaxWaitTime(time.Duration(2 * time.Second))
	//client.SetTimeout(2 * time.Second)
}

func NeStatusEnumToStr(intStatus int) string {
	switch intStatus {
	case 0:
		return "active"
	case 1:
		return "offline"
	case 2:
		return "standby"
	case 3:
		return "maintain"
	default:
		return "unkown"
	}
}

// Get system state from NF/NFs
func GetOneLicenseInfoFromNF(w http.ResponseWriter, r *http.Request) {
	log.Debug("GetOneLicenseInfoFromNF processing... ")

	data := make([]map[string]interface{}, 0)

	vars := mux.Vars(r)
	neType := vars["neType"]
	neId := vars["neId"]
	if neType == "" || neId == "" {
		services.ResponseNotFound404UriNotExist(w, r)
		return
	}
	token, err := services.CheckFrontValidRequest(w, r)
	if err != nil {
		log.Error("Request error:", err)
		return
	}
	log.Debug("AccessToken:", token)

	neInfo, err := dborm.XormGetNeInfo(neType, neId)
	if err != nil {
		log.Error("Failed to XormGetNeInfo:", err)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	} else if neInfo == nil {
		err := global.ErrCMNotFoundTargetNE
		log.Error(global.ErrCMNotFoundTargetNE)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	}
	log.Trace("neInfo:", neInfo)

	//systemState := make(map[string]interface{})
	systemState := &SysState{}
	result := make(map[string]interface{})
	//sysInfo := &SystemInfo{}
	omcNeTypeLower := "omc"
	if config.GetYamlConfig().OMC.NeType != "" {
		omcNeTypeLower = strings.ToLower(config.GetYamlConfig().OMC.NeType)
	}
	if neType != omcNeTypeLower {
		log.Debugf("r.RemoteAddr: %s omcNeTypeLower: %s", r.RemoteAddr, omcNeTypeLower)
		var requestURI2NF string
		if config.GetYamlConfig().OMC.TestMode && strings.ToLower(neType) != "udm" {
			var udmNEs []dborm.NeInfo
			err := dborm.XormGetNeInfoByNeType("UDM", &udmNEs)
			if err != nil {
				log.Error("Get system state from NF is failed:", err)
				services.ResponseInternalServerError500ProcessError(w, err)
				return
			}
			if len(udmNEs) > 0 {
				udmNe := udmNEs[0]
				hostUri := fmt.Sprintf("http://%s:%v", udmNe.Ip, udmNe.Port)
				requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
					hostUri, strings.ToLower(udmNe.NeType))
			}
		} else {
			hostUri := fmt.Sprintf("http://%s:%v", neInfo.Ip, neInfo.Port)
			requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
				hostUri, strings.ToLower(neInfo.NeType))
		}
		log.Debug("requestURI2NF:", requestURI2NF)

		resp, err := client.R().
			EnableTrace().
			SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
			SetHeaders(map[string]string{"accessToken": token}).
			SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
			SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
			Get(requestURI2NF)
		if err != nil {
			log.Error("Failed to get system state:", err)
			services.ResponseInternalServerError500ProcessError(w, err)
			return
		} else {
			log.Trace("resp.Body():", string(resp.Body()))
			_ = json.Unmarshal(resp.Body(), &systemState)

			log.Trace("systemState:", systemState)
			capUsed := config.TDatas[neInfo.NeType].CapUsed
			log.Tracef("neInfo.NeType:%s capUsed: %v", capUsed)
			licenseInfo := &LicenseInfo{
				NeType:         neInfo.NeType,
				NeId:           neInfo.NeId,
				SerialNum:      systemState.SerialNum,
				Capability:     systemState.Capability,
				CapUsed:        capUsed,
				FeatureEnabled: config.TDatas[neInfo.NeType].FeatureEnabled,
				ExpiryDate:     systemState.ExpiryDate,
			}
			//neItem := strings.ToUpper(neType) + "/" + neId
			result, err = global.ToMap(*licenseInfo, "json")
			if err != nil {
				log.Warn("Failed to map:", err)
			}
		}
	} else {
		systemState := GetEMSState(neInfo.Ip)
		licenseInfo := &LicenseInfo{
			NeType:         neInfo.NeType,
			NeId:           neInfo.NeId,
			SerialNum:      systemState.SerialNum,
			Capability:     systemState.Capability,
			CapUsed:        config.TDatas[neInfo.NeType].CapUsed,
			FeatureEnabled: config.TDatas[neInfo.NeType].FeatureEnabled,
			ExpiryDate:     systemState.ExpiryDate,
		}
		result, err = global.ToMap(*licenseInfo, "json")
		if err != nil {
			log.Warn("Failed to map:", err)
		}
		// neItem := strings.ToUpper(neType) + "/" + neId
		// result[neItem] = sysInfo
	}

	data = append(data, result)
	log.Trace("data:", data)

	var response Response
	response.Data = data
	services.ResponseWithJson(w, http.StatusOK, response)
}

// Get system state from NF/NFs
func GetAllLicenseInfoFromNF(w http.ResponseWriter, r *http.Request) {
	log.Debug("GetAllLicenseInfoFromNF processing... ")

	data := make([]map[string]interface{}, 0)

	token, err := services.CheckFrontValidRequest(w, r)
	if err != nil {
		log.Error("Request error:", err)
		return
	}
	log.Debug("AccessToken:", token)

	var neList []dborm.NeInfo
	_, err = dborm.XormGetAllNeInfo(&neList)
	if err != nil {
		log.Error("Failed to XormGetAllNeInfo:", err)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	}
	omcNeTypeLower := "omc"
	if config.GetYamlConfig().OMC.NeType != "" {
		omcNeTypeLower = strings.ToLower(config.GetYamlConfig().OMC.NeType)
	}

	for _, ne := range neList {
		result := make(map[string]interface{})
		log.Debugf("r.RemoteAddr: %s omcNeTypeLower: %s", r.RemoteAddr, omcNeTypeLower)
		log.Debug("ne: ", ne)
		//if strings.ToLower(ne.NeType) != omcNeTypeLower || !strings.Contains(r.RemoteAddr, ne.Ip) {
		if strings.ToLower(ne.NeType) != omcNeTypeLower {
			// hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
			// requestURI2NF := fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
			// 	hostUri, strings.ToLower(ne.NeType))
			var requestURI2NF string
			if config.GetYamlConfig().OMC.TestMode && strings.ToLower(ne.NeType) != "udm" {
				var udmNEs []dborm.NeInfo
				err := dborm.XormGetNeInfoByNeType("UDM", &udmNEs)
				if err != nil {
					log.Error("Get system state from NF is failed:", err)
					services.ResponseInternalServerError500ProcessError(w, err)
					return
				}
				if len(udmNEs) > 0 {
					udmNe := udmNEs[0]
					hostUri := fmt.Sprintf("http://%s:%v", udmNe.Ip, udmNe.Port)
					requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
						hostUri, strings.ToLower(udmNe.NeType))
				}
			} else {
				hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
				requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
					hostUri, strings.ToLower(ne.NeType))
			}
			log.Debug("requestURI2NF:", requestURI2NF)

			resp, err := client.SetTimeout(time.Duration(1 * time.Second)).R().
				EnableTrace().
				SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
				SetHeaders(map[string]string{"accessToken": token}).
				SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
				SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
				Get(requestURI2NF)
			if err != nil {
				log.Error("Get system state from NF is failed:", err)
				// errorMessage := services.ErrorMessage{
				// 	ErrorCode: "1", ErrorInfo: "Internal server error, NF connnect refused",
				// }
				// //result["error"] = errorMessage
				continue
			} else {
				systemState := &SysState{}
				_ = json.Unmarshal(resp.Body(), &systemState)
				licenseInfo := &LicenseInfo{
					NeType:         ne.NeType,
					NeId:           ne.NeId,
					SerialNum:      systemState.SerialNum,
					Capability:     systemState.Capability,
					CapUsed:        config.TDatas[ne.NeType].CapUsed,
					FeatureEnabled: config.TDatas[ne.NeType].FeatureEnabled,
					ExpiryDate:     systemState.ExpiryDate,
				}
				result, err = global.ToMap(*licenseInfo, "json")
				if err != nil {
					log.Warn("Failed to map:", err)
				}
				// neItem := strings.ToUpper(ne.NeType) + "/" + ne.NeId
				// result[neItem] = sysInfo
			}
		} else {
			systemState := GetEMSState(ne.Ip)
			licenseInfo := &LicenseInfo{
				NeType:         ne.NeType,
				NeId:           ne.NeId,
				SerialNum:      systemState.SerialNum,
				Capability:     systemState.Capability,
				CapUsed:        config.TDatas[ne.NeType].CapUsed,
				FeatureEnabled: config.TDatas[ne.NeType].FeatureEnabled,
				ExpiryDate:     systemState.ExpiryDate,
			}
			result, err = global.ToMap(*licenseInfo, "json")
			if err != nil {
				log.Warn("Failed to map:", err)
			}
			// neItem := strings.ToUpper(ne.NeType) + "/" + ne.NeId
			// result[neItem] = sysInfo
		}

		data = append(data, result)
		log.Trace("data:", data)
	}

	var response Response
	response.Data = data
	services.ResponseWithJson(w, http.StatusOK, response)
}

// Get system state from NF/NFs
func GetOneSysinfoFromNF(w http.ResponseWriter, r *http.Request) {
	log.Debug("GetOneSysinfoFromNF processing... ")

	data := make([]map[string]interface{}, 0)

	vars := mux.Vars(r)
	neType := vars["neType"]
	neId := vars["neId"]
	if neType == "" || neId == "" {
		services.ResponseNotFound404UriNotExist(w, r)
		return
	}
	token, err := services.CheckFrontValidRequest(w, r)
	if err != nil {
		log.Error("Request error:", err)
		return
	}
	log.Debug("AccessToken:", token)

	neInfo, err := dborm.XormGetNeInfo(neType, neId)
	if err != nil {
		log.Error("Failed to XormGetNeInfo:", err)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	} else if neInfo == nil {
		err := global.ErrCMNotFoundTargetNE
		log.Error(global.ErrCMNotFoundTargetNE)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	}
	log.Trace("neInfo:", neInfo)

	//systemState := make(map[string]interface{})
	systemState := &SysState{}
	result := make(map[string]interface{})
	//sysInfo := &SystemInfo{}
	omcNeTypeLower := "omc"
	if config.GetYamlConfig().OMC.NeType != "" {
		omcNeTypeLower = strings.ToLower(config.GetYamlConfig().OMC.NeType)
	}
	if neType != omcNeTypeLower {
		log.Debugf("r.RemoteAddr: %s omcNeTypeLower: %s", r.RemoteAddr, omcNeTypeLower)
		var requestURI2NF string
		if config.GetYamlConfig().OMC.TestMode && strings.ToLower(neType) != "udm" {
			var udmNEs []dborm.NeInfo
			err := dborm.XormGetNeInfoByNeType("UDM", &udmNEs)
			if err != nil {
				log.Error("Get system state from NF is failed:", err)
				services.ResponseInternalServerError500ProcessError(w, err)
				return
			}
			if len(udmNEs) > 0 {
				udmNe := udmNEs[0]
				hostUri := fmt.Sprintf("http://%s:%v", udmNe.Ip, udmNe.Port)
				requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
					hostUri, strings.ToLower(udmNe.NeType))
			}
		} else {
			hostUri := fmt.Sprintf("http://%s:%v", neInfo.Ip, neInfo.Port)
			requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
				hostUri, strings.ToLower(neInfo.NeType))
		}
		log.Debug("requestURI2NF:", requestURI2NF)

		resp, err := client.R().
			EnableTrace().
			SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
			SetHeaders(map[string]string{"accessToken": token}).
			SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
			SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
			Get(requestURI2NF)
		if err != nil {
			log.Error("Get system state from NF is failed:", err)
			services.ResponseInternalServerError500ProcessError(w, err)
			return
		} else {
			log.Trace("resp.Body():", string(resp.Body()))
			_ = json.Unmarshal(resp.Body(), &systemState)

			log.Trace("systemState:", systemState)
			hostName := "5gc"
			if systemState.HostName != "" {
				hostName = systemState.HostName
			}
			osInfo := "Linux 5gc 4.15.0-29-generic #31-Ubuntu SMP Tue Jul 17 15:39:52 UTC 2018 x86_64 GNU/Linux"
			if systemState.OsInfo != "" {
				osInfo = systemState.OsInfo
			}
			dbInfo := "adb v1.0.1"
			if systemState.OsInfo != "" {
				dbInfo = systemState.DbInfo
			}
			port, _ := strconv.Atoi(neInfo.Port)
			cpus := 4
			if systemState.HardwareInfo.CPUs != 0 {
				cpus = systemState.HardwareInfo.CPUs
			}
			totalMem := 34029125632
			if systemState.HardwareInfo.Memory != 0 {
				totalMem = systemState.HardwareInfo.Memory
			}
			sysInfo := &SystemInfo{
				NeType:   neInfo.NeType,
				NeId:     neInfo.NeId,
				HostName: hostName,
				OsInfo:   osInfo,
				DbInfo:   dbInfo,
				Version:  systemState.Version,
				IpAddr:   neInfo.Ip,
				Port:     uint16(port),
				CPUs:     cpus,
				TotalMem: totalMem,
				PvFlag:   neInfo.PvFlag,
				Status:   NeStatusEnumToStr(neInfo.Status),
			}
			//neItem := strings.ToUpper(neType) + "/" + neId
			result, err = global.ToMap(*sysInfo, "json")
			if err != nil {
				log.Warn("Failed to map:", err)
			}
		}
	} else {
		systemState := GetEMSState(neInfo.Ip)
		sysInfo := &SystemInfo{
			NeType:   neInfo.NeType,
			NeId:     neInfo.NeId,
			HostName: systemState.HostName,
			OsInfo:   systemState.OsInfo,
			DbInfo:   systemState.DbInfo,
			Version:  systemState.Version,
			IpAddr:   neInfo.Ip,
			Port:     systemState.Port,
			CPUs:     systemState.HardwareInfo.CPUs,
			TotalMem: systemState.HardwareInfo.Memory,
			PvFlag:   neInfo.PvFlag,
			Status:   NeStatusEnumToStr(neInfo.Status),
		}
		result, err = global.ToMap(*sysInfo, "json")
		if err != nil {
			log.Warn("Failed to map:", err)
		}
		// neItem := strings.ToUpper(neType) + "/" + neId
		// result[neItem] = sysInfo
	}

	data = append(data, result)
	log.Trace("data:", data)

	var response Response
	response.Data = data
	services.ResponseWithJson(w, http.StatusOK, response)
}

// Get system state from NF/NFs
func GetAllSysinfoFromNF(w http.ResponseWriter, r *http.Request) {
	log.Debug("GetAllSysinfoFromNF processing... ")

	data := make([]map[string]interface{}, 0)

	token, err := services.CheckFrontValidRequest(w, r)
	if err != nil {
		log.Error("Request error:", err)
		return
	}
	log.Debug("AccessToken:", token)

	var neList []dborm.NeInfo
	_, err = dborm.XormGetAllNeInfo(&neList)
	if err != nil {
		log.Error("Failed to XormGetAllNeInfo:", err)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	}
	omcNeTypeLower := "omc"
	if config.GetYamlConfig().OMC.NeType != "" {
		omcNeTypeLower = strings.ToLower(config.GetYamlConfig().OMC.NeType)
	}

	for _, ne := range neList {
		result := make(map[string]interface{})
		log.Debugf("r.RemoteAddr: %s omcNeTypeLower: %s", r.RemoteAddr, omcNeTypeLower)
		log.Debug("ne: ", ne)
		//if strings.ToLower(ne.NeType) != omcNeTypeLower || !strings.Contains(r.RemoteAddr, ne.Ip) {
		if strings.ToLower(ne.NeType) != omcNeTypeLower {
			// hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
			// requestURI2NF := fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
			// 	hostUri, strings.ToLower(ne.NeType))
			var requestURI2NF string
			if config.GetYamlConfig().OMC.TestMode && strings.ToLower(ne.NeType) != "udm" {
				var udmNEs []dborm.NeInfo
				err := dborm.XormGetNeInfoByNeType("UDM", &udmNEs)
				if err != nil {
					log.Error("Get system state from NF is failed:", err)
					services.ResponseInternalServerError500ProcessError(w, err)
					return
				}
				if len(udmNEs) > 0 {
					udmNe := udmNEs[0]
					hostUri := fmt.Sprintf("http://%s:%v", udmNe.Ip, udmNe.Port)
					requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
						hostUri, strings.ToLower(udmNe.NeType))
				}
			} else {
				hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
				requestURI2NF = fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
					hostUri, strings.ToLower(ne.NeType))
			}
			log.Debug("requestURI2NF:", requestURI2NF)

			resp, err := client.SetTimeout(time.Duration(1 * time.Second)).R().
				EnableTrace().
				SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
				SetHeaders(map[string]string{"accessToken": token}).
				SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
				SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
				Get(requestURI2NF)
			if err != nil {
				log.Error("Get system state from NF is failed:", err)
				// errorMessage := services.ErrorMessage{
				// 	ErrorCode: "1", ErrorInfo: "Internal server error, NF connnect refused",
				// }
				// //result["error"] = errorMessage
				continue
			} else {
				systemState := &SysState{}
				_ = json.Unmarshal(resp.Body(), &systemState)
				hostName := "5gc"
				if systemState.HostName != "" {
					hostName = systemState.HostName
				}
				osInfo := "Linux 5gc 4.15.0-29-generic #31-Ubuntu SMP Tue Jul 17 15:39:52 UTC 2018 x86_64 GNU/Linux"
				if systemState.OsInfo != "" {
					osInfo = systemState.OsInfo
				}
				dbInfo := "adb v1.0.1"
				if systemState.OsInfo != "" {
					dbInfo = systemState.DbInfo
				}
				port, _ := strconv.Atoi(ne.Port)
				cpus := 4
				if systemState.HardwareInfo.CPUs != 0 {
					cpus = systemState.HardwareInfo.CPUs
				}
				totalMem := 34029125632
				if systemState.HardwareInfo.Memory != 0 {
					totalMem = systemState.HardwareInfo.Memory
				}
				sysInfo := &SystemInfo{
					NeType:   ne.NeType,
					NeId:     ne.NeId,
					HostName: hostName,
					OsInfo:   osInfo,
					DbInfo:   dbInfo,
					Version:  systemState.Version,
					IpAddr:   ne.Ip,
					Port:     uint16(port),
					CPUs:     cpus,
					TotalMem: totalMem,
					PvFlag:   ne.PvFlag,
					Status:   NeStatusEnumToStr(ne.Status),
				}
				// neItem := strings.ToUpper(ne.NeType) + "/" + ne.NeId
				// result[neItem] = sysInfo
				result, err = global.ToMap(*sysInfo, "json")
				if err != nil {
					log.Warn("Failed to map:", err)
				}
			}
		} else {
			port, _ := strconv.Atoi(ne.Port)
			systemState := GetEMSState(ne.Ip)
			sysInfo := &SystemInfo{
				NeType:   ne.NeType,
				NeId:     ne.NeId,
				HostName: systemState.HostName,
				OsInfo:   systemState.OsInfo,
				DbInfo:   systemState.DbInfo,
				Version:  systemState.Version,
				IpAddr:   ne.Ip,
				Port:     (uint16(port)),
				CPUs:     systemState.HardwareInfo.CPUs,
				TotalMem: systemState.HardwareInfo.Memory,
				PvFlag:   ne.PvFlag,
				Status:   NeStatusEnumToStr(ne.Status),
			}
			// neItem := strings.ToUpper(ne.NeType) + "/" + ne.NeId
			// result[neItem] = sysInfo
			result, err = global.ToMap(*sysInfo, "json")
			if err != nil {
				log.Warn("Failed to map:", err)
			}
		}

		data = append(data, result)
		log.Trace("data:", data)
	}

	var response Response
	response.Data = data
	services.ResponseWithJson(w, http.StatusOK, response)
}

// Get system state from NF/NFs
func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
	log.Debug("GetStateFromNF processing... ")

	vars := mux.Vars(r)
	neType := vars["elementTypeValue"]
	neType = strings.ToLower(neType)

	if neType == "" {
		services.ResponseNotFound404UriNotExist(w, r)
		return
	}

	if neType == "all" {
		var neList []dborm.NeInfo
		_, err := dborm.XormGetAllNeInfo(&neList)
		if err != nil {
			log.Error("Failed to get all ne info:", err)
			services.ResponseInternalServerError500ProcessError(w, err)
			return
		}
		data := make([]map[string]interface{}, 0)
		for _, ne := range neList {
			result := make(map[string]interface{})
			hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
			requestURI2NF := fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
				hostUri, strings.ToLower(ne.NeType))
			log.Debug("requestURI2NF:", requestURI2NF)

			result["ipAddress"] = ne.Ip
			resp, err := client.R().
				EnableTrace().
				SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
				//SetHeaders(map[string]string{"accessToken": token}).
				SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
				SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
				Get(requestURI2NF)
			if err != nil {
				log.Error("Get system state from NF is failed:", err)
				errorMessage := services.ErrorMessage{
					ErrorCode: "1", ErrorInfo: "Internal server error, NF connnect refused",
				}
				result["error"] = errorMessage
				SN, Version, _ := dborm.XormGetNEStateInfo(ne.NeType, ne.NeId)
				result["serialNum"] = SN
				result["version"] = Version
			} else {
				systemState := make(map[string]interface{})
				_ = json.Unmarshal(resp.Body(), &systemState)
				result["systemState"] = systemState
			}

			neItem := strings.ToUpper(ne.NeType) + "/" + ne.NeId
			mapState := make(map[string]interface{})
			mapState[neItem] = result
			data = append(data, mapState)
			log.Trace("data:", data)
		}
		var response Response
		response.Data = data
		services.ResponseWithJson(w, http.StatusOK, response)
		return
	}

	if neType == "omc" {
		emsState := GetEMSState("127.0.0.1")
		services.ResponseWithJson(w, http.StatusOK, emsState)
		return
	}

	var neList []dborm.NeInfo
	err := dborm.XormGetNeInfoByNeType(neType, &neList)
	if err != nil {
		log.Error("Failed to dborm.XormGetNeInfoByNeType:", err)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	}
	data := make([]map[string]interface{}, 0)
	for _, ne := range neList {
		hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
		requestURI2NF := fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
			hostUri, strings.ToLower(ne.NeType))
		log.Debug("requestURI2NF:", requestURI2NF)

		resp, err := client.R().
			EnableTrace().
			SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
			SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
			Get(requestURI2NF)
		if err != nil {
			log.Error("Get system state from NF is failed:", err)
		} else {
			systemState := make(map[string]interface{})
			_ = json.Unmarshal(resp.Body(), &systemState)
			data = append(data, systemState)
		}
	}

	if len(data) == 1 {
		services.ResponseWithJson(w, http.StatusOK, data[0])
		return
	}
	var response Response
	response.Data = data
	services.ResponseWithJson(w, http.StatusOK, response)
}

// GetStateFromNF 旧函数
// Get system state from NF/NFs
func GetStateFromNFOld(w http.ResponseWriter, r *http.Request) {
	log.Debug("GetStateFromNF processing... ")

	data := make([]map[string]interface{}, 0)

	vars := mux.Vars(r)
	neType := vars["elementTypeValue"]

	var neList []dborm.NeInfo
	if neType == "" {
		services.ResponseNotFound404UriNotExist(w, r)
		return
	}
	token, err := services.CheckFrontValidRequest(w, r)
	if err != nil {
		log.Error("Request error:", err)
		return
	}
	log.Debug("AccessToken:", token)

	switch strings.ToLower(neType) {
	case "all":
		// query all NFs
		// create rest client
		restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
		getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
			config.GetYamlConfig().Database.Name)
		getNeInfoURI := restHostPort + getNeInfoPattern + "?WHERE=status+in+('0','3')"
		log.Debug("getNeInfoPattern:", getNeInfoPattern)

		resp, err := client.R().
			EnableTrace().
			SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
			SetHeaders(map[string]string{"AccessToken": token}).
			SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
			SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
			Get(getNeInfoURI)
		if err != nil {
			log.Error("Get ne_info from DB is failed:", err)
			services.ResponseInternalServerError500NFConnectRefused(w)
			return
		}

		neList, _ = dborm.XormParseResult(resp.Body())
	default:
		restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
		getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
			config.GetYamlConfig().Database.Name)
		getNeInfoURI := restHostPort + getNeInfoPattern
		neId := services.GetUriParamString(r, "ne_id", ",", true, true)
		if neId == "" {
			getNeInfoURI = getNeInfoURI + fmt.Sprintf("?WHERE=status+in+('0','3')+and+ne_type='%s'", neType)
		} else {
			getNeInfoURI = getNeInfoURI + fmt.Sprintf("?WHERE=status+in+('0','3')+and+ne_type='%v'+and+ne_id+in+%v", neType, neId)
		}
		log.Debug("getNeInfoURI:", getNeInfoURI)

		resp, err := client.R().
			EnableTrace().
			SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
			SetHeaders(map[string]string{"accessToken": token}).
			SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
			SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
			Get(getNeInfoURI)
		if err != nil {
			log.Error("Get ne_info from DB is failed:", err)
			services.ResponseInternalServerError500NFConnectRefused(w)
			return
		}

		neList, _ = dborm.XormParseResult(resp.Body())
	}
	omcNeTypeLower := "omc"
	if config.GetYamlConfig().OMC.NeType != "" {
		omcNeTypeLower = strings.ToLower(config.GetYamlConfig().OMC.NeType)
	}
	for _, ne := range neList {
		result := make(map[string]interface{})
		log.Debugf("r.RemoteAddr: %s omcNeTypeLower: %s", r.RemoteAddr, omcNeTypeLower)
		log.Trace("ne: ", ne)
		//if strings.ToLower(ne.NeType) != omcNeTypeLower || !strings.Contains(r.RemoteAddr, ne.Ip) {
		if strings.ToLower(ne.NeType) != omcNeTypeLower {
			hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
			requestURI2NF := fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
				hostUri, ne.NeType)
			log.Debug("requestURI2NF:", requestURI2NF)

			result["ipAddress"] = ne.Ip
			resp, err := client.R().
				EnableTrace().
				SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
				SetHeaders(map[string]string{"accessToken": token}).
				SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
				SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
				Get(requestURI2NF)
			if err != nil {
				log.Error("Get system state from NF is failed:", err)
				errorMessage := services.ErrorMessage{
					ErrorCode: "1", ErrorInfo: "Internal server error, NF connnect refused",
				}
				result["error"] = errorMessage
				SN, Version, _ := dborm.XormGetNEStateInfo(ne.NeType, ne.NeId)
				result["serialNum"] = SN
				result["version"] = Version
			} else {
				systemState := make(map[string]interface{})
				_ = json.Unmarshal(resp.Body(), &systemState)
				result["systemState"] = systemState
			}
		} else {
			result["ipAddress"] = ne.Ip
			emsState := GetEMSState(ne.Ip)
			result["systemState"] = emsState
		}
		neItem := strings.ToUpper(ne.NeType) + "/" + ne.NeId
		mapState := make(map[string]interface{})
		mapState[neItem] = result

		data = append(data, mapState)
		log.Trace("data:", data)
	}

	var response Response
	response.Data = data
	services.ResponseWithJson(w, http.StatusOK, response)
}

func GetEMSState(ip string) *SysState {
	log.Debug("GetEMSState processing... ")

	sysInfo := new(SysInfo)
	err := GetSysInfo(sysInfo)
	if err != nil {
		log.Error("Failed to GetSysInfo:", err)
		return nil
	}

	cpuUsage := &CpuUsage{
		NfCpuUsage:  sysInfo.MyCpuPercent,
		SysCpuUsage: sysInfo.SysCpuPercent,
	}

	memUsage := &MemUsage{
		TotalMem:    sysInfo.SysTotalRam,
		NfUsedMem:   sysInfo.MyUsedRam,
		SysMemUsage: sysInfo.SysRamUsedPercent,
	}

	diskSpace := &DiskSpace{
		PartitionNum:  sysInfo.PartitionNum,
		PartitionInfo: sysInfo.PartitionInfo,
	}

	// 获取主机的 IP 地址列表
	ipAddrs := []string{ip}
	if ip == "" || ip == "127.0.0.1" {
		ipAddrs = []string{}
		interfaces, err := net.Interfaces()
		if err == nil {
			for _, iface := range interfaces {
				for _, v := range iface.Addrs {
					name := iface.Name
					if name[len(name)-1] == '0' {
						name = name[0 : len(name)-1]
						name = strings.Trim(name, "")
					}
					// ignore localhost
					if name == "lo" {
						continue
					}
					prefix := strings.Split(v.Addr, "/")[0]
					if strings.Contains(prefix, "::") {
						ipAddrs = append(ipAddrs, prefix)
					}
					if strings.Contains(prefix, ".") {
						ipAddrs = append(ipAddrs, prefix)
					}
				}
			}
		}
	}

	version := "16.1.1"
	if global.Version != "" {
		version = global.Version
	}
	hostName, _ := os.Hostname()
	dbInfo, _ := dborm.XormGetMySQLVersion()
	emsState := &SysState{
		HostName:     hostName,
		OsInfo:       getUnameStr(),
		DbInfo:       dbInfo,
		IpAddr:       ipAddrs,
		Port:         config.GetYamlConfig().Rest[0].Port,
		Version:      version,
		Capability:   9999999,
		SerialNum:    config.GetYamlConfig().OMC.Sn,
		ExpiryDate:   "-",
		HardwareInfo: HardwareInfo{CPUs: getCpuNumber(), Memory: getTotalMemory()},
		CpuUsage:     *cpuUsage,
		MemUsage:     *memUsage,
		DiskSpace:    *diskSpace,
	}

	//getSystemInfo()
	return emsState
}
