package replay

import (
	"context"
	"errors"
	"fmt"
	"testing"

	"git.vakhrushev.me/av/healthlog/internal/fold"
	"git.vakhrushev.me/av/healthlog/internal/hae"
	"git.vakhrushev.me/av/healthlog/internal/store"
)

// Классификация исхода — та половина, которую пересборка и фоновый воркер
// обязаны делить. Проверяется перебором классов, без базы и без архива: второй
// классификатор разошёлся бы с первым молча, а по счётчику `partial`
// принимается решение о судьбе тела в архиве.
func TestClassifyРазводитИсходыПоКлассам(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		err  error
		want Outcome
	}{
		{"успех", nil, Outcome{Folded: 1}},
		{"слой не выведен", hae.ErrLayerUnknown, Outcome{FailedLayer: 1}},
		{"слой не выведен, обёрнут", fmt.Errorf("свёртка: %w", hae.ErrLayerUnknown), Outcome{FailedLayer: 1}},
		{"содержимое не разбирается", hae.ErrMalformed, Outcome{FailedMalformed: 1}},
		{"база занята", store.ErrBusy, Outcome{Deferred: 1}},
		{"база занята, обёрнута", fmt.Errorf("слияние: %w", store.ErrBusy), Outcome{Deferred: 1}},
		{"работу прекратили снаружи", context.Canceled, Outcome{Deferred: 1}},
		// Дедлайн — свойство доставки, а не обстоятельств: она не уложится в
		// бюджет и в следующий раз, а повтор безнадёжного останавливает очередь.
		{"свёртка не уложилась в бюджет", context.DeadlineExceeded, Outcome{FailedOther: 1}},
		{"прочее", errors.New("диск отвалился"), Outcome{FailedOther: 1}},
		// Паника — дефект нашего кода, а не обстоятельство: доставка выводится
		// из очереди, тело ждёт пересборки.
		{"свёртка паниковала", fold.ErrPanicked, Outcome{FailedOther: 1}},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()

			if got := classify(c.err); got != c.want {
				t.Errorf("Classify(%v) = %+v, ожидалось %+v", c.err, got, c.want)
			}
		})
	}
}

// Накопление — сложение по классам: у пересборки и у воркера один набор имён
// для одних исходов.
func TestOutcomeAddСкладываетПоКлассам(t *testing.T) {
	t.Parallel()

	var total Outcome
	total.Add(Outcome{Folded: 1, Partial: 1})
	total.Add(Outcome{Folded: 1, Incomparable: 2})
	total.Add(Outcome{Deferred: 1})

	want := Outcome{Folded: 2, Deferred: 1, Partial: 1, Incomparable: 2}
	if total != want {
		t.Errorf("сумма %+v, ожидалась %+v", total, want)
	}
}
