package main

import (
	"strings"
	"testing"
	"unicode/utf8"
)

func TestWfWrapSplitsLongText(t *testing.T) {
	longText := "Это довольно длинный текст, который заведомо не поместится в одну строку узкого терминала и должен быть перенесён на несколько строк."
	// termWidth=50 -> реальная ширина переноса 50-4=46 (выше минимума
	// в 40, так что запасной вариант 60 тут не сработает)
	wrapped := wfWrap(longText, 50)
	lines := strings.Split(strings.TrimRight(wrapped, "\n"), "\n")
	if len(lines) < 2 {
		t.Fatalf("ожидался перенос минимум на 2 строки, получена 1: %q", wrapped)
	}
	for _, line := range lines {
		if n := utf8.RuneCountInString(line); n > 46 {
			t.Errorf("строка длиннее заданной ширины 46 рун: %q (рун: %d)", line, n)
		}
	}
}

func TestWfWrapShortTextStaysOnOneLine(t *testing.T) {
	short := "Короткая фраза."
	wrapped := wfWrap(short, 80)
	lines := strings.Split(strings.TrimRight(wrapped, " \n"), "\n")
	if len(lines) != 1 {
		t.Errorf("короткий текст не должен переноситься, получено %d строк: %q", len(lines), wrapped)
	}
}

func TestWfWrapUsesFallbackWidthWhenTermWidthUnset(t *testing.T) {
	longText := strings.Repeat("слово ", 30)
	wrapped := wfWrap(longText, 0)
	lines := strings.Split(strings.TrimRight(wrapped, " \n"), "\n")
	if len(lines) < 2 {
		t.Errorf("даже с запасной шириной длинный текст должен был перенестись, получено: %q", wrapped)
	}
}

func TestWayfarerQuestOfferAccept(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	m.pendingQuestKey = WayfarerQuestDefs[0].Key
	m.view = wfViewQuestOffer

	next, _ := m.Update(key("1"))
	m = next.(WayfarerModel)
	if m.view != wfViewQuestStep {
		t.Fatalf("после принятия задания должен был открыться экран его шага, получено view=%v", m.view)
	}
	if m.ship.ActiveQuest == nil || m.ship.ActiveQuest.Key != WayfarerQuestDefs[0].Key {
		t.Fatalf("задание должно было стать активным")
	}
	if m.pendingQuestKey != "" {
		t.Errorf("pendingQuestKey должен был очиститься после принятия")
	}
}

func TestWayfarerQuestOfferDecline(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	m.pendingQuestKey = WayfarerQuestDefs[0].Key
	m.view = wfViewQuestOffer

	next, _ := m.Update(key("2"))
	m = next.(WayfarerModel)
	if m.view != wfViewHub {
		t.Fatalf("после отказа должен был открыться хаб, получено view=%v", m.view)
	}
	if m.ship.ActiveQuest != nil {
		t.Errorf("задание не должно было стать активным после отказа")
	}
	if m.pendingQuestKey != "" {
		t.Errorf("pendingQuestKey должен был очиститься после отказа")
	}
}

func TestWayfarerMarketPricesStableUntilNextDocking(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	m.view = wfViewMarket

	before := append([]float64{}, m.marketPrices...)
	for i := 0; i < 10; i++ {
		next, _ := m.Update(key("down"))
		m = next.(WayfarerModel)
		next, _ = m.Update(key("up"))
		m = next.(WayfarerModel)
	}
	for i, p := range m.marketPrices {
		if p != before[i] {
			t.Errorf("цена товара %d изменилась просто от навигации по рынку: было %v, стало %v", i, before[i], p)
		}
	}
}

func TestWayfarerMarketPricesChangeAfterJump(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	before := append([]float64{}, m.marketPrices...)

	nearby := wayfarerNearbySystems(m.ship.CurrentSystem, m.ship.FuelRange())
	if len(nearby) == 0 {
		t.Fatal("нет достижимых систем для проверки")
	}
	next, _ := m.performJump(nearby[0])
	m2 := next.(WayfarerModel)

	changed := false
	for i, p := range m2.marketPrices {
		if p != before[i] {
			changed = true
			break
		}
	}
	if !changed {
		t.Errorf("после прыжка в новую систему цены должны были пересчитаться")
	}
}

func TestWayfarerBuySellUsesCachedPriceNotFreshRoll(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	m.view = wfViewMarket
	m.cursor = 0
	cachedPrice := m.marketPrices[0]

	next, _ := m.Update(key("b"))
	m = next.(WayfarerModel)
	// после покупки кэш цены не должен был поменяться сам по себе
	if m.marketPrices[0] != cachedPrice {
		t.Errorf("покупка не должна была пересчитать цену: было %v, стало %v", cachedPrice, m.marketPrices[0])
	}
	wantCredits := 1000 - cachedPrice
	if m.ship.Credits != wantCredits {
		t.Errorf("покупка должна была списать ровно закэшированную цену %v, кредитов осталось %v (ожидалось %v)",
			cachedPrice, m.ship.Credits, wantCredits)
	}
}

func TestWayfarerResetRequestShowsConfirmation(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	next, _ := m.Update(key("N"))
	m = next.(WayfarerModel)
	if !m.confirmingReset {
		t.Fatalf("ожидался запрос подтверждения после нажатия N")
	}
}

func TestWayfarerResetCancelKeepsCareer(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	m.ship.Credits = 5000

	next, _ := m.Update(key("N"))
	m = next.(WayfarerModel)
	next, _ = m.Update(key("n"))
	m = next.(WayfarerModel)

	if m.confirmingReset {
		t.Errorf("запрос подтверждения должен был закрыться после отмены")
	}
	if m.ship.Credits != 5000 {
		t.Errorf("карьера не должна была сброситься при отмене, получено кредитов: %v", m.ship.Credits)
	}
}

func TestWayfarerResetConfirmStartsFreshCareer(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	m.ship.Credits = 5000
	m.ship.CurrentSystem = WayfarerSystemID{Galaxy: 2, Index: 50}

	next, _ := m.Update(key("N"))
	m = next.(WayfarerModel)
	next, _ = m.Update(key("y"))
	m = next.(WayfarerModel)

	if m.confirmingReset {
		t.Errorf("запрос подтверждения должен был закрыться после подтверждения")
	}
	if m.ship.Credits != 1000 {
		t.Errorf("ожидались стартовые 1000 кредитов после новой карьеры, получено %v", m.ship.Credits)
	}
	if m.ship.CurrentSystem != wayfarerStartSystem {
		t.Errorf("новая карьера должна была начаться со стартовой системы")
	}
	if m.view != wfViewHub {
		t.Errorf("после сброса должен был открыться хаб")
	}
}

func TestWayfarerResetActionsIgnoredWhileConfirming(t *testing.T) {
	withTempConfigDir(t)
	m := NewWayfarerModel()
	viewBefore := m.view

	next, _ := m.Update(key("N"))
	m = next.(WayfarerModel)
	// нажатие "1" (переход на рынок) не должно срабатывать, пока ждём подтверждения
	next, _ = m.Update(key("1"))
	m = next.(WayfarerModel)
	if m.view != viewBefore {
		t.Errorf("обычная навигация не должна срабатывать во время запроса подтверждения")
	}
}

func TestWfWrapClampsExcessiveWidth(t *testing.T) {
	text := "текст"
	wrapped := wfWrap(text, 500)
	firstLine := strings.Split(wrapped, "\n")[0]
	// при очень широком терминале перенос всё равно не должен
	// растягиваться дальше 100 колонок (проверяем по длине padding'а,
	// которым lipgloss дополняет строку до ширины стиля)
	if n := utf8.RuneCountInString(firstLine); n > 100 {
		t.Errorf("ширина не должна была превышать 100 колонок даже на широком терминале, получено %d", n)
	}
}
