package stages

import (
	"fmt"
	"gitea.anxinyun.cn/container/common_models"
	"gitea.anxinyun.cn/container/common_utils/configLoad"
	"log"
	"time"
)

// 阶段处理
type Stage struct {
	Name         string
	In           <-chan []*common_models.ProcessData
	processFuncs []func([]*common_models.ProcessData) []*common_models.ProcessData
	Out          chan []*common_models.ProcessData
	execOver     chan bool //阶段执行完毕,用以排查超时
}

func NewStage(name string) *Stage {
	stageBufSize := configLoad.LoadConfig().GetInt64("performance.node.stageBufSize")

	return &Stage{
		Name:         name,
		processFuncs: make([]func([]*common_models.ProcessData) []*common_models.ProcessData, 0),
		In:           make(<-chan []*common_models.ProcessData, stageBufSize),
		Out:          make(chan []*common_models.ProcessData, stageBufSize),
		execOver:     make(chan bool, 1),
	}
}

func (s *Stage) StageRun() <-chan []*common_models.ProcessData {
	go func() {
		defer func() {
			close(s.Out)
			log.Printf("[%s]关闭out", s.Name)
		}()

		for n := range s.In {
			log.Printf("[%s]接收数据 In[%p] 通道长度=%d/%d", s.Name, s.In, len(s.In), cap(s.In))
			result := s.process(n)
			s.Out <- result
		}
		log.Printf("%s over", s.Name)
	}()

	return s.Out
}

func (s *Stage) AddProcess(fun func([]*common_models.ProcessData) []*common_models.ProcessData) {
	s.processFuncs = append(s.processFuncs, fun)
}

func (s *Stage) process(data []*common_models.ProcessData) []*common_models.ProcessData {
	go s.handlerTimeOutCheck(s.Name, "批量处理耗时跟踪")

	for _, processFunc := range s.processFuncs {
		//tag := fmt.Sprintf("%d/%d", i+1, len(s.processFuncs))
		log.Printf("stage[%s] start, len(data)=%d", s.Name, len(data))
		func() {
			defer timeCost(s.Name, fmt.Sprintf("ProcessData数组元素个数[%d]", len(data)), time.Now())
			data = processFunc(data)
		}()
		log.Printf("stage[%s] over, len(data)=%d", s.Name, len(data))
	}

	s.execOver <- true
	return data
}

func (s *Stage) handlerTimeOutCheck(stageName, deviceId string) {
	stageTimeout := configLoad.LoadConfig().GetInt64("performance.node.stageTimeout")
	defaultTimeout := time.Duration(stageTimeout) * time.Second
	select {
	case <-s.execOver:
	case <-time.After(defaultTimeout):
		log.Printf("=====================")
		//TODO #TEST BEGIN 测试时可以注释掉下面这行,否则调试时超时,会引发一个 panic,导致程序中断。
		log.Panicf("stage[%s] ->[%s] 流程处理,超时[%v],请排查", stageName, deviceId, defaultTimeout)
	}
}
func timeCost(nodeId, deviceId string, start time.Time) {
	tc := time.Since(start)
	log.Printf("stage[%s] ->[%s] 耗时 = %v", nodeId, deviceId, tc)
}