package schedule

import (
	"context"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/config"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/constants"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/message"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/models"
	"github.com/tidbcloud/observability/o11ymetering/pkg/facade"
	"github.com/tidbcloud/observability/o11ymetering/pkg/service/statistic"
	"github.com/tidbcloud/observability/pkg/common/configs"
)

func StatisticTask() Task {
	start := models.Period(config.GetPeriodConfig().FirstPeriod).StartTime().Unix()
	return Task{
		Name:          "statistic",
		start:         start,
		interval:      time.Duration(config.GetScheduleConfig().ExecuteStatisticTaskIntervalSeconds) * time.Second,
		execute:       ExecuteStatisticTask,
		nextTimestamp: NextExecuteTimestamp,
		ready:         ExecuteReady,
		uploadData:    true,
	}
}

func ExecuteStatisticTask(ctx context.Context, timestamp int64) error {
	facade.SwitchStatisticReaderWriter(ctx, models.FromStartTime(time.Unix(timestamp, 0)))

	_, err := statistic.GenerateStatisticTask(context.TODO(), message.GenerateStatisticTaskReq{
		Period: string(models.FromStartTime(time.Unix(timestamp, 0))),
	})

	if err != nil {
		panic(err)
	}
	tasks, pickErr := facade.GetCurrentStatisticReaderWriter().PickEarliestPendingTasks(ctx, configs.GetServiceInfo().Region, 100)
	if pickErr != nil {
		return pickErr
	}
	if len(tasks) > 0 {
		var ch = make(chan int, len(tasks))
		for i, task := range tasks {
			go func() {
				startAt := time.Now()
				_, executeErr := statistic.ExecuteStatisticTask(ctx, message.ExecuteStatisticTaskReq{
					ID: task.ID,
				})

				if executeErr != nil {
					log.Errorf("execute statistic task %s failed, period = %s, err = %s", task.ID, task.Period, executeErr.Error())
				} else {
					duration := time.Now().Unix() - startAt.Unix()
					log.Infof("execute statistic task %s succeed, duration = %d", task.ID, duration)
				}
				ch <- i
			}()
		}

		for i := 0; i < len(tasks); i++ {
			<-ch
		}
	} else {
		log.Infof("no pending statistic task")
	}

	return nil
}

func NextExecuteTimestamp(ctx context.Context, checkpoint int64, start int64) int64 {
	if checkpoint+start == 0 {
		return time.Now().Unix()
	}

	if start > checkpoint+constants.StatisticStepSeconds {
		return start
	} else {
		return checkpoint + constants.StatisticStepSeconds
	}
}

func ExecuteReady(ctx context.Context, timestamp int64) bool {
	if timestamp+constants.StatisticStepSeconds*2 > time.Now().Unix() {
		return false
	}

	return true
}
