package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v3"
)

// ПРИМЕЧАНИЕ: помимо 8 встроенных квестов (см. wayfarer_quests.go),
// можно добавлять собственные квесты, не трогая код коллекции —
// достаточно положить .yaml файл в директорию "quests" рядом с
// исполняемым файлом (или в текущей рабочей директории). Формат
// файла и полное описание — в rules.go (правила Странника) и в
// wayfarer_quest_loader_example.yaml.
//
// Загрузка максимально терпима к ошибкам: файл с некорректным
// синтаксисом, отсутствующими полями или неполным переводом просто
// пропускается с предупреждением в stderr — никогда не приводит к
// падению всей программы. Ключ квеста, уже занятый одним из
// встроенных (или другим уже загруженным файлом), тоже пропускается
// с предупреждением, а не молча перезаписывает существующий.

// wfLocalizedText — пара текстов на русском и английском в одном
// YAML-поле.
type wfLocalizedText struct {
	RU string `yaml:"ru"`
	EN string `yaml:"en"`
}

type wayfarerQuestChoiceYAML struct {
	Label        wfLocalizedText `yaml:"label"`
	NextStep     *int            `yaml:"next_step"`
	Terminal     bool            `yaml:"terminal"`
	Success      bool            `yaml:"success"`
	Result       wfLocalizedText `yaml:"result"`
	CreditsDelta float64         `yaml:"credits_delta"`
	HullDelta    int             `yaml:"hull_delta"`
	CargoIdx     *int            `yaml:"cargo_idx"`
	CargoDelta   int             `yaml:"cargo_delta"`
	KillsDelta   int             `yaml:"kills_delta"`
}

type wayfarerQuestStepYAML struct {
	Text    wfLocalizedText           `yaml:"text"`
	Choices []wayfarerQuestChoiceYAML `yaml:"choices"`
}

type wayfarerQuestYAML struct {
	Key   string                  `yaml:"key"`
	Title wfLocalizedText         `yaml:"title"`
	Brief wfLocalizedText         `yaml:"brief"`
	Steps []wayfarerQuestStepYAML `yaml:"steps"`
}

// wfLoadedText — один текст для регистрации в общей карте переводов.
type wfLoadedText struct {
	key, ru, en string
}

// convertYAMLQuest превращает разобранный YAML в WayfarerQuestDef и
// список текстов для регистрации переводов. sourceName — имя файла,
// только для сообщений об ошибках.
func convertYAMLQuest(y wayfarerQuestYAML, sourceName string) (WayfarerQuestDef, []wfLoadedText, error) {
	var def WayfarerQuestDef

	if y.Key == "" {
		return def, nil, fmt.Errorf("%s: не указано поле key", sourceName)
	}
	if len(y.Steps) == 0 {
		return def, nil, fmt.Errorf("%s: квест %q — нет ни одного шага (steps)", sourceName, y.Key)
	}

	def.Key = y.Key
	def.TitleKey = y.Key + ".title"
	def.BriefKey = y.Key + ".brief"

	var texts []wfLoadedText
	texts = append(texts,
		wfLoadedText{def.TitleKey, y.Title.RU, y.Title.EN},
		wfLoadedText{def.BriefKey, y.Brief.RU, y.Brief.EN},
	)

	for si, step := range y.Steps {
		stepTextKey := fmt.Sprintf("%s.step%d", y.Key, si)
		texts = append(texts, wfLoadedText{stepTextKey, step.Text.RU, step.Text.EN})

		if len(step.Choices) == 0 {
			return def, nil, fmt.Errorf("%s: квест %q, шаг %d — нет вариантов выбора (choices)", sourceName, y.Key, si)
		}

		stepOut := WayfarerQuestStep{TextKey: stepTextKey}
		for ci, choice := range step.Choices {
			labelKey := fmt.Sprintf("%s.choice%d.%d", y.Key, si, ci)
			texts = append(texts, wfLoadedText{labelKey, choice.Label.RU, choice.Label.EN})

			choiceOut := WayfarerQuestChoice{
				LabelKey:     labelKey,
				Terminal:     choice.Terminal,
				Success:      choice.Success,
				CreditsDelta: choice.CreditsDelta,
				HullDelta:    choice.HullDelta,
				CargoDelta:   choice.CargoDelta,
				KillsDelta:   choice.KillsDelta,
				CargoIdx:     -1,
			}
			if choice.CargoIdx != nil {
				choiceOut.CargoIdx = *choice.CargoIdx
			}

			if choice.Terminal {
				resultKey := fmt.Sprintf("%s.result%d.%d", y.Key, si, ci)
				texts = append(texts, wfLoadedText{resultKey, choice.Result.RU, choice.Result.EN})
				choiceOut.ResultKey = resultKey
			} else {
				if choice.NextStep == nil {
					return def, nil, fmt.Errorf("%s: квест %q, шаг %d, выбор %d — не указан next_step (обязателен, если это не конец квеста)",
						sourceName, y.Key, si, ci)
				}
				if *choice.NextStep < 0 || *choice.NextStep >= len(y.Steps) {
					return def, nil, fmt.Errorf("%s: квест %q, шаг %d, выбор %d — next_step %d вне диапазона шагов (0..%d)",
						sourceName, y.Key, si, ci, *choice.NextStep, len(y.Steps)-1)
				}
				choiceOut.NextStep = *choice.NextStep
			}
			stepOut.Choices = append(stepOut.Choices, choiceOut)
		}
		def.Steps = append(def.Steps, stepOut)
	}

	return def, texts, nil
}

// LoadWayfarerQuestsFromDir читает все *.yaml/*.yml файлы в dir и
// возвращает успешно разобранные квесты и список предупреждений по
// пропущенным файлам (пустая/отсутствующая директория — не ошибка,
// просто нет пользовательских квестов). existingKeys — ключи, уже
// занятые (встроенными или ранее загруженными квестами); функция не
// мутирует эту карту.
func LoadWayfarerQuestsFromDir(dir string, existingKeys map[string]bool) ([]WayfarerQuestDef, []string) {
	entries, err := os.ReadDir(dir)
	if err != nil {
		return nil, nil
	}

	taken := make(map[string]bool, len(existingKeys))
	for k := range existingKeys {
		taken[k] = true
	}

	var loaded []WayfarerQuestDef
	var warnings []string

	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}
		name := entry.Name()
		if !strings.HasSuffix(name, ".yaml") && !strings.HasSuffix(name, ".yml") {
			continue
		}
		path := filepath.Join(dir, name)
		data, err := os.ReadFile(path)
		if err != nil {
			warnings = append(warnings, fmt.Sprintf("%s: не удалось прочитать файл: %v", name, err))
			continue
		}

		var y wayfarerQuestYAML
		if err := yaml.Unmarshal(data, &y); err != nil {
			warnings = append(warnings, fmt.Sprintf("%s: ошибка разбора YAML: %v", name, err))
			continue
		}

		def, texts, err := convertYAMLQuest(y, name)
		if err != nil {
			warnings = append(warnings, err.Error())
			continue
		}

		if taken[def.Key] {
			warnings = append(warnings, fmt.Sprintf("%s: квест с ключом %q уже занят, файл пропущен", name, def.Key))
			continue
		}

		incomplete := false
		for _, tx := range texts {
			if tx.ru == "" || tx.en == "" {
				warnings = append(warnings, fmt.Sprintf("%s: у ключа %q не заполнен перевод (ru=%q en=%q)", name, tx.key, tx.ru, tx.en))
				incomplete = true
			}
		}
		if incomplete {
			continue
		}

		for _, tx := range texts {
			translations[tx.key] = translationEntry{ru: tx.ru, en: tx.en}
		}
		loaded = append(loaded, def)
		taken[def.Key] = true
	}

	return loaded, warnings
}

// loadCustomWayfarerQuests ищет пользовательские квесты в директории
// "quests" рядом с исполняемым файлом и в текущей рабочей директории
// (та же логика поиска, что и у .env для сида галактики), добавляя
// найденные к WayfarerQuestDefs. Предупреждения о пропущенных файлах
// выводятся в stderr, не мешая TUI.
func loadCustomWayfarerQuests() {
	existingKeys := make(map[string]bool, len(WayfarerQuestDefs))
	for _, q := range WayfarerQuestDefs {
		existingKeys[q.Key] = true
	}

	for _, dir := range wayfarerEnvSearchDirs() {
		questsDir := filepath.Join(dir, "quests")
		loaded, warnings := LoadWayfarerQuestsFromDir(questsDir, existingKeys)
		for _, w := range warnings {
			fmt.Fprintln(os.Stderr, "квесты: "+w)
		}
		for _, q := range loaded {
			WayfarerQuestDefs = append(WayfarerQuestDefs, q)
			existingKeys[q.Key] = true
		}
	}
}
