package infra

import (
	"os/exec"
	"runtime"
	"time"

	"go.uber.org/atomic"

	"virjar.com/majora-go/log"
	"virjar.com/majora-go/model"
)

const (
	cmdWin  = "/C"
	cmdUnix = "-c"
)

type PPPRedial struct {
	inRedialing *atomic.Bool
}

func NewPPPRedial() *PPPRedial {
	return &PPPRedial{
		inRedialing: atomic.NewBool(false),
	}
}

func (p *PPPRedial) Redial(cfg *model.Configure) {
	if p.inRedialing.CAS(false, true) {
		log.Run().Infof("[Redial] start")
		beforeIp := GetPPP()
		retry := 0
		defer func(start time.Time) {
			newIp := GetPPP()
			log.Run().Infof("[Redial] retry %d, cost %v, ip change %s -> %s ",
				retry, time.Since(start), beforeIp, newIp)
		}(time.Now())
		for {
			retry++
			status := command(cfg)
			pingBaidu := RandomPing()
			log.Run().Infof("[Redial] net check: %d->%v", retry, pingBaidu)
			if pingBaidu && status {
				break
			}
		}
		p.inRedialing.CAS(true, false)
	} else {
		log.Run().Infof("[Redial] inRedialing ignore this")
	}
}

func (p *PPPRedial) RedialByCheck(cfg *model.Configure) {
	p.Redial(cfg)
}

func command(cfg *model.Configure) bool {
	execPath := cfg.Redial.ExecPath
	if len(execPath) == 0 {
		log.Run().Warn("[Redial] exec file is empty")
		return true
	}
	command := cfg.Redial.Command
	if len(command) == 0 {
		log.Run().Warn("[Redial] command is empty")
		return true
	}

	args := cmdUnix
	if runtime.GOOS == "windows" {
		args = cmdWin
	}

	cmd := exec.Command(command, args, execPath)
	output, err := cmd.Output()
	if err != nil {
		log.Run().Errorf("[Redial] Execute Shell:%s failed with error:%s", command, err.Error())
		return false
	}
	log.Run().Infof("[Redial] success %+v resp:%s", cmd, string(output))
	return true
}
