package job

import (
	"context"
	"time"

	log "github.com/sirupsen/logrus"

	"github.com/tidbcloud/auto-deploy/internal/metrics"
	"github.com/tidbcloud/auto-deploy/internal/types"
)

func RunDeployJobs(region *types.Region) error {
	startedAt := time.Now()
	log.WithFields(log.Fields{
		"job-path": region.JobPath,
		"k8s-name": region.K8s.Name,
	}).Info("Run jobs")
	kubeconfig, err := region.CloudProvider.GetKubeConfig(&region.K8s)
	if err != nil {
		return err
	}

	previousOutputs := make(map[string]string)
	for jobName, output := range region.JobOutputs {
		previousOutputs[jobName] = output.Output
	}

	runner, err := NewJobRunner(kubeconfig, region, previousOutputs)
	if err != nil {
		return err
	}

	runner.RunAll(context.TODO(), region.LatestRunId)
	// only succeed
	err = setJobOutputs(runner, region)
	setJobMetrics(runner, startedAt)

	return err
}

func RunFinalizerJobs(region *types.Region) error {
	log.WithFields(log.Fields{
		"job-path": region.JobPath,
		"k8s-name": region.K8s.Name,
	}).Info("Run finalizer jobs")
	kubeconfig, err := region.CloudProvider.GetKubeConfig(&region.K8s)
	if err != nil {
		return err
	}

	previousOutputs := make(map[string]string)
	for jobName, output := range region.JobOutputs {
		previousOutputs[jobName] = output.Output
	}

	runner, err := NewJobRunner(kubeconfig, region, previousOutputs)
	if err != nil {
		return err
	}

	runner.RunFinalizers(context.TODO())
	return getFinalizerError(runner)
}

func getFinalizerError(runner *JobRunner) error {
	for _, finalizer := range runner.finalizers {
		if finalizer.State == JOB_STATE_FAILED {
			return finalizer.Error
		}
	}
	return nil
}

func setJobOutputs(runner *JobRunner, region *types.Region) (err error) {
	for jobName, job := range runner.jobs {
		if job.State == JOB_STATE_FAILED {
			err = job.Error
			continue
		}

		if job.State == JOB_STATE_SUCCEEDED {
			jobOutput := types.JobOutput{
				Output:  job.OutputPath,
				Version: job.Version,
			}
			log.WithFields(log.Fields{
				"name":    jobName,
				"output":  job.OutputPath,
				"version": job.Version,
			}).Debug("Set job output")
			region.JobOutputs[jobName] = jobOutput
		}
	}
	return
}

func setJobMetrics(runner *JobRunner, startedAt time.Time) {
	total, succeed, failed, skipped, depended_failed := 0, 0, 0, 0, 0
	for jobName, job := range runner.jobs {
		total += 1
		labels := map[string]string{
			"job-name":    jobName,
			"job-version": job.Version,
		}
		metrics.AddMetricWithLabels(metrics.METRIC_JOB_STATUS, float64(job.State), labels)
		switch job.State {
		case JOB_STATE_FAILED:
			failed += 1
			metrics.AddMetricWithLabels(metrics.METRIC_JOB_DURATION, job.Duration.Seconds(), labels)
		case JOB_STATE_SUCCEEDED:
			succeed += 1
			metrics.AddMetricWithLabels(metrics.METRIC_JOB_DURATION, job.Duration.Seconds(), labels)
		case JOB_STATE_DEPENDED_JOB_FAILED:
			depended_failed += 1
		case JOB_STATE_SKIPPED:
			skipped += 1
		}
	}
	metrics.AddMetric(metrics.METRIC_JOBS_TOTAL_DURATION, time.Since(startedAt).Seconds())
	metrics.AddMetric(metrics.METRIC_TOTAL_JOBS, float64(total))
	metrics.AddMetric(metrics.METRIC_SUCCEED_JOBS, float64(succeed))
	metrics.AddMetric(metrics.METRIC_FAILED_JOBS, float64(failed))
	metrics.AddMetric(metrics.METRIC_DEPENDED_FAILED_JOBS, float64(depended_failed))
	metrics.AddMetric(metrics.METRIC_SKIPPED_JOBS, float64(skipped))
}
