package containers

import (
	"fmt"
	"github.com/coroot/coroot-node-agent/flags"
	log "github.com/sirupsen/logrus"
	"os"
	"time"
)

func (r *Registry) DoFusing() error {
	log.Infof("-----DoFusing will to execute fuse operates -----\n")
	var (
		lastErr error
		err     error
	)
	if r.isFusing {
		log.Infof("[fusing] Euspace is already in a state of fuse.")
		return nil
	}
	//先处于熔断状态 (应用层的uprobes\stackUprobes将进行Detach操作)
	r.isFusing = true
	for tryLeft := *flags.FuseTryMax; tryLeft > 0; tryLeft-- {
		lastErr = nil
		//再停止数据发送
		if err = os.Setenv("SEND", ""); err != nil {
			lastErr = fmt.Errorf("failed to set SEND env: %w", err)
			log.Warnf("DoFusing,FuseTryMax[%d],tryLeft[%d] set SEND env occurs error:%s", *flags.FuseTryMax, tryLeft-1, err.Error())
		} else {
			log.Infof("DoFusing,FuseTryMax[%d],tryLeft[%d] set SEND env OK", *flags.FuseTryMax, tryLeft-1)
		}
		//最后关闭内核层的tracepoint、kprobe
		if err = r.tracer.UnlinkEbpfProg(); err != nil {
			lastErr = fmt.Errorf("failed to unlink eBPF program: %w", err)
			log.Warnf("DoFusing,FuseTryMax[%d],tryLeft[%d] UnlinkEbpfProg occurs error:%s", *flags.FuseTryMax, tryLeft-1, err.Error())
		} else {
			log.Infof("DoFusing,FuseTryMax[%d],tryLeft[%d] UnlinkEbpfProg OK", *flags.FuseTryMax, tryLeft-1)
		}

		if lastErr == nil {
			break
		}

		time.Sleep(time.Duration(100) * time.Millisecond)
	}

	if lastErr != nil {
		//重试N次后，仍然失败 设置熔断机制异常标识 (后续就不进行恢复操作了)
		r.IsFuseException = true
		log.Warnf("DoFusing,lastErr is not nil, set IsFuseException = true. error: %s", lastErr.Error())
	}
	return lastErr
}

func (r *Registry) DoResume() error {
	log.Infof("-----DoResume will to execute resume operates -----\n")
	var (
		lastErr error
		err     error
	)
	if !r.isFusing {
		log.Infof("[resume] Euspace is already in a state of resume.")
		return nil
	}
	for tryLeft := *flags.FuseTryMax; tryLeft > 0; tryLeft-- {
		//先开启内核层的tracepoint、kprobe
		if err = r.tracer.LinkEbpfProg(); err != nil {
			lastErr = fmt.Errorf("failed to link eBPF program: %w", err)
			log.Warnf("DoResume,FuseTryMax[%d],tryLeft[%d] LinkEbpfProg occurs error:%s", *flags.FuseTryMax, tryLeft-1, err.Error())
		} else {
			log.Infof("DoResume,FuseTryMax[%d],tryLeft[%d] LinkEbpfProg OK", *flags.FuseTryMax, tryLeft-1)
		}
		//再开启数据发送
		if err = os.Setenv("SEND", "1"); err != nil {
			lastErr = fmt.Errorf("failed to set SEND env: %w", err)
			log.Warnf("DoResume,FuseTryMax[%d],tryLeft[%d] set SEND env occurs error:%s", *flags.FuseTryMax, tryLeft-1, err.Error())
		} else {
			log.Infof("DoResume,FuseTryMax[%d],tryLeft[%d] set SEND env OK", *flags.FuseTryMax, tryLeft-1)
		}

		if lastErr == nil {
			break
		}

		time.Sleep(time.Duration(100) * time.Millisecond)
	}

	if lastErr != nil {
		//重试N次后，仍然失败 将状态设置为熔断，设置熔断机制异常标识 (后续就不进行熔断操作了)
		r.IsFuseException = true
		r.isFusing = true
		log.Warnf("DoResume,lastErr is not nil, set IsFuseException = true. error: %s", lastErr.Error())
	} else {
		//最后处于非熔断状态 (应用层的uprobes\stackUprobes将进行Attach操作)
		r.isFusing = false
	}
	return lastErr
}
