package services

import (
	"crawlab/constants"
	"crawlab/lib/cron"
	"crawlab/model"
	"github.com/apex/log"
	uuid "github.com/satori/go.uuid"
	"runtime/debug"
)

var Sched *Scheduler

type Scheduler struct {
	cron *cron.Cron
}

func AddTask(s model.Schedule) func() {
	return func() {
		nodeId := s.NodeId

		// 生成任务ID
		id := uuid.NewV4()

		// 生成任务模型
		t := model.Task{
			Id:       id.String(),
			SpiderId: s.SpiderId,
			NodeId:   nodeId,
			Status:   constants.StatusPending,
		}

		// 将任务存入数据库
		if err := model.AddTask(t); err != nil {
			log.Errorf(err.Error())
			debug.PrintStack()
			return
		}

		// 加入任务队列
		if err := AssignTask(t); err != nil {
			log.Errorf(err.Error())
			debug.PrintStack()
			return
		}
	}
}

func UpdateSchedules() {
	if err := Sched.Update(); err != nil {
		log.Errorf(err.Error())
		return
	}
}

func (s *Scheduler) Start() error {
	exec := cron.New(cron.WithSeconds())

	// 启动cron服务
	s.cron.Start()

	// 更新任务列表
	if err := s.Update(); err != nil {
		return err
	}

	// 每30秒更新一次任务列表
	spec := "*/30 * * * * *"
	if _, err := exec.AddFunc(spec, UpdateSchedules); err != nil {
		return err
	}

	return nil
}

func (s *Scheduler) AddJob(job model.Schedule) error {
	spec := job.Cron

	// 添加任务
	eid, err := s.cron.AddFunc(spec, AddTask(job))
	if err != nil {
		return err
	}

	// 更新EntryID
	job.EntryId = eid
	if err := job.Save(); err != nil {
		return err
	}

	return nil
}

func (s *Scheduler) RemoveAll() {
	entries := s.cron.Entries()
	for i := 0; i < len(entries); i++ {
		s.cron.Remove(entries[i].ID)
	}
}

func (s *Scheduler) Update() error {
	// 删除所有定时任务
	s.RemoveAll()

	// 获取所有定时任务
	sList, err := model.GetScheduleList(nil)
	if err != nil {
		return err
	}

	// 遍历任务列表
	for i := 0; i < len(sList); i++ {
		// 单个任务
		job := sList[i]

		// 添加到定时任务
		if err := s.AddJob(job); err != nil {
			return err
		}
	}

	return nil
}

func InitScheduler() error {
	Sched = &Scheduler{
		cron: cron.New(cron.WithSeconds()),
	}
	if err := Sched.Start(); err != nil {
		return err
	}
	return nil
}
