package main

import (
	"os"
	"path/filepath"
	"testing"
)

func intPtr(n int) *int { return &n }

func validYAMLQuest() wayfarerQuestYAML {
	return wayfarerQuestYAML{
		Key:   "quest.test_valid",
		Title: wfLocalizedText{RU: "Тестовый квест", EN: "Test Quest"},
		Brief: wfLocalizedText{RU: "Краткое описание", EN: "Brief description"},
		Steps: []wayfarerQuestStepYAML{
			{
				Text: wfLocalizedText{RU: "Ситуация на шаге 0", EN: "Situation at step 0"},
				Choices: []wayfarerQuestChoiceYAML{
					{Label: wfLocalizedText{RU: "Идти дальше", EN: "Go on"}, NextStep: intPtr(1)},
					{Label: wfLocalizedText{RU: "Отступить", EN: "Retreat"}, Terminal: true, Success: false,
						Result: wfLocalizedText{RU: "Провал", EN: "Failure"}},
				},
			},
			{
				Text: wfLocalizedText{RU: "Ситуация на шаге 1", EN: "Situation at step 1"},
				Choices: []wayfarerQuestChoiceYAML{
					{Label: wfLocalizedText{RU: "Завершить", EN: "Finish"}, Terminal: true, Success: true,
						Result:       wfLocalizedText{RU: "Успех", EN: "Success"},
						CreditsDelta: 500, HullDelta: -5},
				},
			},
		},
	}
}

func TestConvertYAMLQuestValid(t *testing.T) {
	def, texts, err := convertYAMLQuest(validYAMLQuest(), "test.yaml")
	if err != nil {
		t.Fatalf("неожиданная ошибка: %v", err)
	}
	if def.Key != "quest.test_valid" {
		t.Errorf("неверный ключ квеста: %q", def.Key)
	}
	if len(def.Steps) != 2 {
		t.Fatalf("ожидалось 2 шага, получено %d", len(def.Steps))
	}
	if def.Steps[0].Choices[0].NextStep != 1 {
		t.Errorf("ожидался NextStep=1 у первого выбора")
	}
	if def.Steps[0].Choices[1].Terminal != true || def.Steps[0].Choices[1].Success != false {
		t.Errorf("второй выбор должен быть терминальным провалом")
	}
	if def.Steps[1].Choices[0].CreditsDelta != 500 || def.Steps[1].Choices[0].HullDelta != -5 {
		t.Errorf("не перенеслись числовые последствия выбора")
	}
	if def.Steps[0].Choices[0].CargoIdx != -1 {
		t.Errorf("ожидался CargoIdx=-1 по умолчанию, получено %d", def.Steps[0].Choices[0].CargoIdx)
	}
	if len(texts) == 0 {
		t.Errorf("ожидались тексты для регистрации переводов")
	}
}

func TestConvertYAMLQuestMissingKey(t *testing.T) {
	y := validYAMLQuest()
	y.Key = ""
	if _, _, err := convertYAMLQuest(y, "test.yaml"); err == nil {
		t.Errorf("ожидалась ошибка при отсутствующем key")
	}
}

func TestConvertYAMLQuestNoSteps(t *testing.T) {
	y := validYAMLQuest()
	y.Steps = nil
	if _, _, err := convertYAMLQuest(y, "test.yaml"); err == nil {
		t.Errorf("ожидалась ошибка при отсутствии шагов")
	}
}

func TestConvertYAMLQuestNoChoicesInStep(t *testing.T) {
	y := validYAMLQuest()
	y.Steps[0].Choices = nil
	if _, _, err := convertYAMLQuest(y, "test.yaml"); err == nil {
		t.Errorf("ожидалась ошибка при отсутствии вариантов выбора на шаге")
	}
}

func TestConvertYAMLQuestMissingNextStepOnNonTerminal(t *testing.T) {
	y := validYAMLQuest()
	y.Steps[0].Choices[0].NextStep = nil
	if _, _, err := convertYAMLQuest(y, "test.yaml"); err == nil {
		t.Errorf("ожидалась ошибка при отсутствии next_step у нетерминального выбора")
	}
}

func TestConvertYAMLQuestNextStepOutOfRange(t *testing.T) {
	y := validYAMLQuest()
	y.Steps[0].Choices[0].NextStep = intPtr(99)
	if _, _, err := convertYAMLQuest(y, "test.yaml"); err == nil {
		t.Errorf("ожидалась ошибка при next_step вне диапазона")
	}
}

func TestConvertYAMLQuestExplicitCargoIdx(t *testing.T) {
	y := validYAMLQuest()
	y.Steps[1].Choices[0].CargoIdx = intPtr(2)
	y.Steps[1].Choices[0].CargoDelta = -3
	def, _, err := convertYAMLQuest(y, "test.yaml")
	if err != nil {
		t.Fatalf("неожиданная ошибка: %v", err)
	}
	if def.Steps[1].Choices[0].CargoIdx != 2 || def.Steps[1].Choices[0].CargoDelta != -3 {
		t.Errorf("явно указанный cargo_idx/cargo_delta не сохранился")
	}
}

func writeYAMLFile(t *testing.T, dir, name, content string) {
	t.Helper()
	if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
		t.Fatalf("не удалось записать тестовый файл: %v", err)
	}
}

func TestLoadWayfarerQuestsFromDirMissingDirIsHarmless(t *testing.T) {
	loaded, warnings := LoadWayfarerQuestsFromDir(filepath.Join(t.TempDir(), "does-not-exist"), nil)
	if loaded != nil || warnings != nil {
		t.Errorf("отсутствующая директория должна давать (nil, nil), получено (%v, %v)", loaded, warnings)
	}
}

func TestLoadWayfarerQuestsFromDirValidFile(t *testing.T) {
	dir := t.TempDir()
	writeYAMLFile(t, dir, "myquest.yaml", `
key: quest.loader_test_ok
title: {ru: "Загруженный квест", en: "Loaded Quest"}
brief: {ru: "Описание", en: "Brief"}
steps:
  - text: {ru: "Шаг 0", en: "Step 0"}
    choices:
      - label: {ru: "Дальше", en: "Onward"}
        next_step: 1
  - text: {ru: "Шаг 1", en: "Step 1"}
    choices:
      - label: {ru: "Конец", en: "The end"}
        terminal: true
        success: true
        result: {ru: "Победа", en: "Victory"}
`)
	loaded, warnings := LoadWayfarerQuestsFromDir(dir, nil)
	if len(warnings) != 0 {
		t.Errorf("не ожидалось предупреждений, получено: %v", warnings)
	}
	if len(loaded) != 1 {
		t.Fatalf("ожидался ровно 1 загруженный квест, получено %d", len(loaded))
	}
	if loaded[0].Key != "quest.loader_test_ok" {
		t.Errorf("неверный ключ загруженного квеста: %q", loaded[0].Key)
	}
	if T("quest.loader_test_ok.title") != "Загруженный квест" {
		t.Errorf("перевод названия не зарегистрировался в общей карте переводов, T() вернул: %q", T("quest.loader_test_ok.title"))
	}
}

func TestLoadWayfarerQuestsFromDirSkipsMalformedYAML(t *testing.T) {
	dir := t.TempDir()
	writeYAMLFile(t, dir, "broken.yaml", "key: [это не валидный YAML для этой структуры\n  - вложенность сломана")
	loaded, warnings := LoadWayfarerQuestsFromDir(dir, nil)
	if len(loaded) != 0 {
		t.Errorf("сломанный файл не должен был загрузиться, получено %d квестов", len(loaded))
	}
	if len(warnings) != 1 {
		t.Fatalf("ожидалось ровно 1 предупреждение, получено %d: %v", len(warnings), warnings)
	}
}

func TestLoadWayfarerQuestsFromDirSkipsDuplicateKey(t *testing.T) {
	dir := t.TempDir()
	writeYAMLFile(t, dir, "dup.yaml", `
key: quest.supernova
title: {ru: "Дубликат", en: "Duplicate"}
brief: {ru: "x", en: "x"}
steps:
  - text: {ru: "x", en: "x"}
    choices:
      - label: {ru: "x", en: "x"}
        terminal: true
        success: true
        result: {ru: "x", en: "x"}
`)
	existing := map[string]bool{"quest.supernova": true}
	loaded, warnings := LoadWayfarerQuestsFromDir(dir, existing)
	if len(loaded) != 0 {
		t.Errorf("квест с уже занятым ключом не должен был загрузиться")
	}
	if len(warnings) != 1 {
		t.Fatalf("ожидалось предупреждение о занятом ключе, получено: %v", warnings)
	}
}

func TestLoadWayfarerQuestsFromDirSkipsIncompleteTranslation(t *testing.T) {
	dir := t.TempDir()
	writeYAMLFile(t, dir, "incomplete.yaml", `
key: quest.loader_test_incomplete
title: {ru: "Есть по-русски", en: ""}
brief: {ru: "x", en: "x"}
steps:
  - text: {ru: "x", en: "x"}
    choices:
      - label: {ru: "x", en: "x"}
        terminal: true
        success: true
        result: {ru: "x", en: "x"}
`)
	loaded, warnings := LoadWayfarerQuestsFromDir(dir, nil)
	if len(loaded) != 0 {
		t.Errorf("квест с неполным переводом не должен был загрузиться")
	}
	if len(warnings) == 0 {
		t.Errorf("ожидалось предупреждение о неполном переводе")
	}
}

func TestLoadWayfarerQuestsFromDirIgnoresNonYAMLFiles(t *testing.T) {
	dir := t.TempDir()
	writeYAMLFile(t, dir, "readme.txt", "это не квест, просто заметка")
	loaded, warnings := LoadWayfarerQuestsFromDir(dir, nil)
	if len(loaded) != 0 || len(warnings) != 0 {
		t.Errorf("файлы не .yaml/.yml должны молча игнорироваться, получено loaded=%v warnings=%v", loaded, warnings)
	}
}

// TestLoadedQuestIsFullyPlayable проверяет, что загруженный из YAML
// квест не просто структурно корректен, а реально проходим через
// обычный игровой цикл (StartQuest -> ChooseQuestOption -> ...).
func TestLoadedQuestIsFullyPlayable(t *testing.T) {
	dir := t.TempDir()
	writeYAMLFile(t, dir, "playable.yaml", `
key: quest.loader_test_playable
title: {ru: "Играбельный квест", en: "Playable Quest"}
brief: {ru: "x", en: "x"}
steps:
  - text: {ru: "Шаг 0", en: "Step 0"}
    choices:
      - label: {ru: "Вариант A", en: "Option A"}
        next_step: 1
  - text: {ru: "Шаг 1", en: "Step 1"}
    choices:
      - label: {ru: "Завершить успешно", en: "Finish successfully"}
        terminal: true
        success: true
        result: {ru: "Готово", en: "Done"}
        credits_delta: 250
`)
	loaded, warnings := LoadWayfarerQuestsFromDir(dir, nil)
	if len(warnings) != 0 {
		t.Fatalf("не ожидалось предупреждений: %v", warnings)
	}
	if len(loaded) != 1 {
		t.Fatalf("ожидался 1 квест, получено %d", len(loaded))
	}

	oldDefs := WayfarerQuestDefs
	WayfarerQuestDefs = append(append([]WayfarerQuestDef{}, oldDefs...), loaded...)
	defer func() { WayfarerQuestDefs = oldDefs }()

	s := NewWayfarerShip(WayfarerSystemID{})
	creditsBefore := s.Credits
	if err := s.StartQuest("quest.loader_test_playable"); err != nil {
		t.Fatalf("неожиданная ошибка запуска квеста: %v", err)
	}
	if _, err := s.ChooseQuestOption(0); err != nil {
		t.Fatalf("неожиданная ошибка выбора на шаге 0: %v", err)
	}
	step, ok := s.CurrentQuestStep()
	if !ok || T(step.TextKey) != "Шаг 1" {
		t.Fatalf("ожидался переход на шаг 1, получено: %+v (ok=%v)", step, ok)
	}
	if _, err := s.ChooseQuestOption(0); err != nil {
		t.Fatalf("неожиданная ошибка завершающего выбора: %v", err)
	}
	if s.ActiveQuest != nil {
		t.Errorf("квест должен был завершиться")
	}
	if !s.CompletedQuests["quest.loader_test_playable"] {
		t.Errorf("квест должен был отметиться как завершённый успешно")
	}
	if s.Credits != creditsBefore+250 {
		t.Errorf("награда за квест не начислилась: было %v, стало %v", creditsBefore, s.Credits)
	}
}
