package info

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/json"
	"net"

	cun "git.loafle.net/commons/util-go/net"
	cung "git.loafle.net/commons/util-go/net/gateway"
	ocmi "git.loafle.net/overflow/commons-go/model/infra"
	ocmm "git.loafle.net/overflow/commons-go/model/meta"
	ocmn "git.loafle.net/overflow/commons-go/model/noauthprobe"
	"github.com/shirou/gopsutil/host"
)

func GetRegistHeader(apiKey string) (string, error) {
	var err error
	nap := ocmn.NoAuthProbe{
		APIKey: apiKey,
	}

	var infraHost *ocmi.InfraHost
	if infraHost, err = getInfraHost(); nil != err {
		return "", err
	}

	var buf []byte
	if buf, err = json.Marshal(infraHost); nil != err {
		return "", err
	}
	nap.InfraHostMeta = string(buf)

	if buf, err = json.Marshal(nap); nil != err {
		return "", err
	}

	var b bytes.Buffer
	gWriter, err := gzip.NewWriterLevel(&b, gzip.BestSpeed)
	if nil != err {
		return "", err
	}

	if _, err := gWriter.Write(buf); nil != err {
		return "", err
	}
	if err := gWriter.Flush(); nil != err {
		return "", err
	}
	if err := gWriter.Close(); nil != err {
		return "", err
	}

	gBuf := b.Bytes()

	enc := base64.StdEncoding.EncodeToString(gBuf)

	return enc, nil
}

func getInfraHost() (*ocmi.InfraHost, error) {
	var err error
	infraHost := &ocmi.InfraHost{}

	if infraHost.InfraHostMachine, err = getInfraHostMachine(); nil != err {
		return nil, err
	}
	if infraHost.InfraHostOS, err = getInfraHostOS(); nil != err {
		return nil, err
	}
	if infraHost.InfraHostIPs, err = getInfraHostIPs(); nil != err {
		return nil, err
	}

	return infraHost, nil
}

func getInfraHostMachine() (*ocmi.InfraHostMachine, error) {
	meta := make(map[string]interface{})

	// if i, err := cpu.Info(); nil == err {
	// 	meta["CPU"] = i
	// }

	// if i, err := mem.SwapMemory(); nil == err {
	// 	meta["Memory_Swap"] = i
	// }

	// if i, err := mem.VirtualMemory(); nil == err {
	// 	meta["Memory_Virtual"] = i
	// }

	// if i, err := disk.Partitions(true); nil == err {
	// 	meta["Partitions"] = i
	// }

	// if i, err := disk.Usage("/"); nil == err {
	// 	meta["Disk_Usage"] = i
	// }

	buf, err := json.Marshal(meta)
	if nil != err {
		return nil, err
	}

	return &ocmi.InfraHostMachine{
		Meta: string(buf),
	}, nil
}

func getInfraHostOS() (*ocmi.InfraHostOS, error) {
	i, err := host.Info()
	if nil != err {
		return nil, err
	}

	infraHostOS := &ocmi.InfraHostOS{}

	infraHostOS.Name = i.Hostname
	infraHostOS.OS = i.OS
	infraHostOS.Platform = i.Platform
	infraHostOS.PlatformFamily = i.PlatformFamily
	infraHostOS.KernelVersion = i.KernelVersion
	infraHostOS.HostID = i.HostID

	return infraHostOS, nil
}

func getInfraHostIPs() ([]*ocmi.InfraHostIP, error) {
	gwIP, gwIface, err := cung.DiscoverGateway()
	if nil != err {
		return nil, err
	}

	interfaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	infraHostIPs := make([]*ocmi.InfraHostIP, 0)

	for _, i := range interfaces {
		if i.Flags&net.FlagLoopback != 0 || i.Flags&net.FlagUp == 0 {
			continue
		}

		addrs, err := i.Addrs()
		if nil != err {
			continue
		}

	LOOP_ADDR:
		for _, addr := range addrs {
			_, ipNet, err := cun.ParseCIDR(addr.String())
			if nil != err {
				continue
			}

			infraHostIP := &ocmi.InfraHostIP{}
			infraHostIP.Address = addr.String()

			switch ipNet.Version() {
			case 4:
				infraHostIP.MetaIPType = ocmm.ToMetaIPType(ocmm.MetaIPTypeEnumV4)
			case 6:
				infraHostIP.MetaIPType = ocmm.ToMetaIPType(ocmm.MetaIPTypeEnumV6)
			default:
				continue LOOP_ADDR
			}

			infraHostIP.Mac = i.HardwareAddr.String()
			infraHostIP.Iface = i.Name

			if gwIface == i.Name {
				infraHostIP.Gateway = gwIP.String()
			}

			infraHostIPs = append(infraHostIPs, infraHostIP)
		}
	}

	return infraHostIPs, nil
}
