package service

import (
	"context"
	"os"
	"testing"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"git.vakhrushev.me/av/transcriber/internal/entity"
)

// Счёт работы конвейера живёт в шаге, а не в воркере: только шаг знает рубеж, с
// которого взята запись, а воркеры пула одинаковы и именем ничего не говорят.

// stageCount читает счётчик работы из общего реестра процесса. Судит реестр, а
// не переменную пакета: метка, потерянная в точке употребления, переменную не
// ломает, а на странице метрик видна.
func stageCount(t *testing.T, stage, errLabel string) float64 {
	t.Helper()

	families, err := prometheus.DefaultGatherer.Gather()
	require.NoError(t, err)

	for _, mf := range families {
		if mf.GetName() != "transcriber_worker_job_count" {
			continue
		}
		for _, m := range mf.GetMetric() {
			var gotStage, gotErr string
			for _, label := range m.GetLabel() {
				switch label.GetName() {
				case "stage":
					gotStage = label.GetValue()
				case "error":
					gotErr = label.GetValue()
				}
			}
			if gotStage == stage && gotErr == errLabel {
				return m.GetCounter().GetValue()
			}
		}
	}
	return 0
}

// Критерий приёмки 11. Отказ виден с разрезом по шагу: счётчик отказов —
// единственный сигнал, по которому владелец сервиса замечает поломку, и без
// метки рубежа «падает приведение» и «падает распознавание» стали бы
// неразличимы.
func TestFailureIsCountedWithStageLabel(t *testing.T) {
	env := newPipelineEnv(t, &okMetaViewer{}, &okConverter{})

	beforeOk := stageCount(t, entity.StateUploaded, "false")

	record := newRecord(t, env)
	require.NoError(t, env.service.RunStep(t.Context()))
	require.Equal(t, entity.StateNormalized, readRecord(t, env, record.Id).State)

	assert.InDelta(t, beforeOk+1, stageCount(t, entity.StateUploaded, "false"), 0,
		"успешный шаг засчитан с меткой своего рубежа")

	// Теперь тот же рубеж, но с отказом.
	failing := newPipelineEnv(t, &okMetaViewer{}, &failingStepConverter{})
	beforeErr := stageCount(t, entity.StateUploaded, "true")

	newRecord(t, failing)
	require.Error(t, failing.service.RunStep(t.Context()))

	assert.InDelta(t, beforeErr+1, stageCount(t, entity.StateUploaded, "true"), 0,
		"отказ засчитан с меткой того же рубежа")
}

// failingStepConverter отказывает **отказом шага**, а не приговором записи:
// приговор счётчик отказов не растит, потому что шаг при нём отрабатывает.
type failingStepConverter struct{}

func (c *failingStepConverter) Convert(_ context.Context, _, dest string) error {
	// Файл результата не создаётся вовсе — шаг отказывает на измерении копии.
	return os.Remove(dest)
}
