package standby_ctrl

import (
	"context"
	"github.com/go-pkgz/cronrange"
	"go.uber.org/zap"
	"sync"
	"time"
)

// Cron 时间控制器
func Cron(rootRules []cronrange.Rule, cron string, play func(c context.Context) error) func(c context.Context) error {
	// 设定默认时间规则
	if cron == "" {
		cron = "* * * *"
	}

	rules, err := cronrange.Parse(cron)
	if err != nil {
		zap.S().Errorln("解析时间规则异常: ", err)
		return nil
	}

	return func(c context.Context) error {
		a := make(chan bool)
		defer close(a)

		zap.S().Infoln("待机时间控制器")
		defer zap.S().Infoln("待机时间控制器结束")

		// 等待组
		var waitGroup sync.WaitGroup
		defer waitGroup.Wait()

		ticker := time.NewTicker(time.Second)
		defer ticker.Stop()

		waitGroup.Add(1)
		go func() {
			defer waitGroup.Done()
			for {
				select {
				case <-c.Done():
					return
				case <-ticker.C:
					if cronrange.Match(rules, time.Now()) && cronrange.Match(rootRules, time.Now()) {
						a <- true
					} else {
						a <- false
					}
				}
			}
		}()

		var cancel context.CancelFunc
		var m sync.Mutex
		for {
			select {
			case <-c.Done():
				if cancel != nil {
					cancel()
					cancel = nil
				}
				return nil
			case r := <-a:
				if r {
					if ok := m.TryLock(); ok {
						ctx, cc := context.WithCancel(context.TODO())
						cancel = cc
						waitGroup.Add(1)
						go func() {
							defer waitGroup.Done()
							defer m.Unlock()
							defer func() { cancel = nil }()

							err := play(ctx)
							if err != nil {
								zap.S().Errorln("执行动作异常: ", err)
								select {
								case <-ctx.Done():
									return
								case <-time.After(time.Minute):
								}
							}
						}()
					}
				} else if cancel != nil {
					cancel()
					cancel = nil
				}
			}
		}
	}
}
