package netmgrd

import (
	"fmt"
	"math"
	"os/exec"
	"sync"
	"time"

	"github.com/beevik/ntp"
	"golang.org/x/sys/unix"
)

var servers = []string{
	"210.72.145.44",
	"ntp.aliyun.com",
	"ntp.ntsc.ac.cn",
}

type ntpResult struct {
	t   time.Time
	d   time.Duration
	err error
}

func SyncNTPTime() error {
	t, err := getAccurateTime(servers, 5*time.Second)
	if err != nil {
		return err
	}
	if err := SetSystemTime(t); err != nil {
		return err
	}
	return nil
}

func getAccurateTime(servers []string, timeout time.Duration) (time.Time, error) {
	ch := make(chan ntpResult, len(servers))
	var wg sync.WaitGroup
	wg.Add(len(servers))

	for _, s := range servers {
		go func(s string) {
			defer wg.Done()

			resp, err := ntp.QueryWithOptions(s, ntp.QueryOptions{Timeout: timeout})
			if err != nil {
				ch <- ntpResult{err: err}
				return
			}

			if resp.Stratum == 0 || resp.Time.IsZero() {
				ch <- ntpResult{err: fmt.Errorf("invalid ntp response")}
				return
			}

			t := resp.Time.Add(resp.RTT / 2)
			d := resp.RootDistance // 最大可能偏差
			ch <- ntpResult{t: t, d: d, err: nil}
		}(s)
	}

	go func() {
		wg.Wait()
		close(ch)
	}()

	var bestTime time.Time
	var bestDist time.Duration = math.MaxInt64

	for r := range ch {
		if r.err != nil {
			continue
		}

		if r.d < bestDist {
			bestDist = r.d
			bestTime = r.t
		}
	}

	if bestTime.IsZero() {
		return time.Time{}, fmt.Errorf("no valid ntp response")
	}

	return bestTime, nil
}

var setTimeMu sync.Mutex // 调用SetSystemTime()函数的并发互斥

func SetSystemTime(t time.Time) error {
	setTimeMu.Lock()
	defer setTimeMu.Unlock()

	ts := unix.NsecToTimespec(t.UnixNano())

	if err := unix.ClockSettime(unix.CLOCK_REALTIME, &ts); err != nil {
		return err
	}

	_ = exec.Command("hwclock", "-w").Run()

	return nil
}
