package main

import "testing"

// TestWayfarerQuestDefsStructuralIntegrity проверяет каждый из
// реальных (не тестовых) квестов на внутреннюю согласованность:
// все NextStep указывают на существующие шаги, все CargoIdx либо -1
// (не используется), либо валидный индекс в WayfarerCommodities, и
// у каждого квеста есть хотя бы один способ дойти до Terminal-выбора
// от стартового шага.
func TestWayfarerQuestDefsStructuralIntegrity(t *testing.T) {
	if len(WayfarerQuestDefs) != 8 {
		t.Fatalf("ожидалось 8 квестов, получено %d", len(WayfarerQuestDefs))
	}

	seenKeys := map[string]bool{}
	for _, q := range WayfarerQuestDefs {
		if q.Key == "" || q.TitleKey == "" || q.BriefKey == "" {
			t.Errorf("квест с пустыми обязательными ключами: %+v", q)
		}
		if seenKeys[q.Key] {
			t.Errorf("дублирующийся ключ квеста: %s", q.Key)
		}
		seenKeys[q.Key] = true

		if len(q.Steps) == 0 {
			t.Errorf("квест %s: должен иметь хотя бы один шаг", q.Key)
		}

		reachesTerminal := false
		for stepIdx, step := range q.Steps {
			if step.TextKey == "" {
				t.Errorf("квест %s, шаг %d: пустой TextKey", q.Key, stepIdx)
			}
			if len(step.Choices) == 0 {
				t.Errorf("квест %s, шаг %d: нет ни одного варианта выбора", q.Key, stepIdx)
			}
			for choiceIdx, c := range step.Choices {
				if c.LabelKey == "" {
					t.Errorf("квест %s, шаг %d, выбор %d: пустой LabelKey", q.Key, stepIdx, choiceIdx)
				}
				if c.CargoIdx != -1 && (c.CargoIdx < 0 || c.CargoIdx >= len(WayfarerCommodities)) {
					t.Errorf("квест %s, шаг %d, выбор %d: недопустимый CargoIdx %d", q.Key, stepIdx, choiceIdx, c.CargoIdx)
				}
				if c.Terminal {
					if c.ResultKey == "" {
						t.Errorf("квест %s, шаг %d, выбор %d: terminal-выбор без ResultKey", q.Key, stepIdx, choiceIdx)
					}
					reachesTerminal = true
				} else {
					if c.NextStep < 0 || c.NextStep >= len(q.Steps) {
						t.Errorf("квест %s, шаг %d, выбор %d: NextStep %d вне диапазона шагов (0..%d)",
							q.Key, stepIdx, choiceIdx, c.NextStep, len(q.Steps)-1)
					}
				}
			}
		}
		if !reachesTerminal {
			t.Errorf("квест %s: ни один выбор во всём квесте не завершает его (нет ни одного Terminal)", q.Key)
		}
	}
}

// TestWayfarerQuestDefsAllTranslated проверяет, что для каждого
// ключа перевода, используемого в квестах, есть запись в таблице
// translations на обоих языках (это ловит опечатки в ключах и
// забытые переводы).
func TestWayfarerQuestDefsAllTranslated(t *testing.T) {
	checkKey := func(key string) {
		t.Helper()
		if key == "" {
			return
		}
		entry, ok := translations[key]
		if !ok {
			t.Errorf("отсутствует перевод для ключа %q", key)
			return
		}
		if entry.ru == "" || entry.en == "" {
			t.Errorf("перевод для ключа %q неполный: ru=%q en=%q", key, entry.ru, entry.en)
		}
	}

	for _, q := range WayfarerQuestDefs {
		checkKey(q.TitleKey)
		checkKey(q.BriefKey)
		for _, step := range q.Steps {
			checkKey(step.TextKey)
			for _, c := range step.Choices {
				checkKey(c.LabelKey)
				checkKey(c.ResultKey)
			}
		}
	}
}

// TestWayfarerQuestDefsPlayableToCompletion проверяет, что каждый
// квест реально проходим от начала до какого-либо Terminal-выбора,
// просто выбирая первый не-terminal вариант на каждом шаге (а если
// таких нет — берёт первый попавшийся) — ловит зависания/бесконечные
// циклы в ручной разметке шагов.
func TestWayfarerQuestDefsPlayableToCompletion(t *testing.T) {
	for _, q := range WayfarerQuestDefs {
		s := NewWayfarerShip(WayfarerSystemID{})
		if err := s.StartQuest(q.Key); err != nil {
			t.Fatalf("квест %s: не удалось начать: %v", q.Key, err)
		}
		const maxSteps = 20
		for i := 0; i < maxSteps && s.ActiveQuest != nil; i++ {
			step, _ := s.CurrentQuestStep()
			choiceIdx := 0
			for idx, c := range step.Choices {
				if !c.Terminal {
					choiceIdx = idx
					break
				}
			}
			if _, err := s.ChooseQuestOption(choiceIdx); err != nil {
				t.Fatalf("квест %s: неожиданная ошибка выбора на шаге: %v", q.Key, err)
			}
		}
		if s.ActiveQuest != nil {
			t.Errorf("квест %s: не завершился за %d шагов (возможен бесконечный цикл)", q.Key, maxSteps)
		}
	}
}
