package worker

import (
	"context"
	"time"

	"github.com/tidbcloud/ecosystem-service/internal/metric"
	"github.com/tidbcloud/ecosystem-service/internal/worker/github_app"
	"github.com/tidbcloud/ecosystem-service/internal/worker/vercel_webhook"
	"github.com/tidbcloud/pkgv2/worker"
	"go.uber.org/zap"
)

type GlobalWorker struct {
	workers []*worker.PeriodWorker
}

func (w *GlobalWorker) Start() {
	for _, worker := range w.workers {
		worker.Start()
	}
}

func NewGlobalWorker(gitHupAppWorker *github_app.GitHubAppWorker,
	cleanWorker *github_app.GitHubAppCleanWorker,
	metricWorker *github_app.GitHubAppMetricWorker,
	webhookWorker *vercel_webhook.VercelWebhookWorker,
	webhookMetricWorker *vercel_webhook.VercelWebhookMetricWorker,
	webhookCleanWorker *vercel_webhook.VercelWebhookCleanWorker,
	logger *zap.Logger,
) *GlobalWorker {
	gWorker := &GlobalWorker{}
	newPeriodWorker := func(workerName string, interval time.Duration, f worker.Handler) *worker.PeriodWorker {
		logger.With(zap.String("worker", workerName), zap.String("interval", interval.String()))
		return worker.NewPeriodWorker(logger, interval, withMetrics(workerName, f))
	}
	gWorker.workers = []*worker.PeriodWorker{
		newPeriodWorker(gitHupAppWorker.GetName(), gitHupAppWorker.GetInterval(), gitHupAppWorker.Run),
		newPeriodWorker(cleanWorker.GetName(), cleanWorker.GetInterval(), cleanWorker.Run),
		newPeriodWorker(metricWorker.GetName(), metricWorker.GetInterval(), metricWorker.Run),
		newPeriodWorker(webhookWorker.GetName(), webhookWorker.GetInterval(), webhookWorker.Run),
		newPeriodWorker(webhookMetricWorker.GetName(), webhookMetricWorker.GetInterval(), webhookMetricWorker.Run),
		newPeriodWorker(webhookCleanWorker.GetName(), webhookCleanWorker.GetInterval(), webhookCleanWorker.Run),
	}

	return gWorker
}

func withMetrics(workerName string, f worker.Handler) worker.Handler {
	return func(ctx context.Context) error {
		tBegin := time.Now()
		status := "ok"
		err := f(ctx)
		if err != nil {
			status = "error"
		}
		duration := time.Since(tBegin).Seconds()
		metric.WorkerRunDuration.WithLabelValues(workerName, status).Observe(duration)
		return err
	}
}
