package plugin

import "testing"

// Phase R6 — zone-condition harvest interactions (§3 zone notes).

func TestZoneConditionHarvestBlock_Underforge(t *testing.T) {
	cases := []struct {
		stacks int
		action HarvestAction
		want   bool
	}{
		{9, HarvestMine, false},
		{10, HarvestMine, true},
		{10, HarvestScavenge, false},
		{0, HarvestMine, false},
	}
	for _, c := range cases {
		exp := &Expedition{ZoneID: ZoneUnderforge, TemporalStack: c.stacks}
		got := zoneConditionHarvestBlock(exp, c.action) != ""
		if got != c.want {
			t.Errorf("Underforge stacks=%d action=%s: got block=%v want=%v", c.stacks, c.action, got, c.want)
		}
	}
}

func TestZoneConditionHarvestBlock_DragonsLair(t *testing.T) {
	exp := &Expedition{ZoneID: ZoneDragonsLair, RegionState: map[string]any{}}
	if zoneConditionHarvestBlock(exp, HarvestMine) != "" {
		t.Errorf("Dragon's Lair pre-awake should not block")
	}
	exp.RegionState["infernax_awake"] = true
	if zoneConditionHarvestBlock(exp, HarvestMine) == "" {
		t.Errorf("Dragon's Lair post-awake should block")
	}
}

func TestZoneConditionHarvestBlock_Abyss(t *testing.T) {
	cases := []struct {
		stack int
		want  bool
	}{
		{60, false},
		{80, false},
		{81, true},
		{99, true},
	}
	for _, c := range cases {
		exp := &Expedition{ZoneID: ZoneAbyssPortal, TemporalStack: c.stack}
		got := zoneConditionHarvestBlock(exp, HarvestEssence) != ""
		if got != c.want {
			t.Errorf("Abyss stack=%d: got block=%v want=%v", c.stack, got, c.want)
		}
	}
}

func TestZoneConditionHarvestDCMod_Underforge(t *testing.T) {
	cases := []struct {
		stacks int
		action HarvestAction
		want   int
	}{
		{6, HarvestMine, 0},
		{7, HarvestMine, 3},
		{9, HarvestMine, 3},
		{7, HarvestScavenge, 0},
	}
	for _, c := range cases {
		exp := &Expedition{ZoneID: ZoneUnderforge, TemporalStack: c.stacks}
		got, _ := zoneConditionHarvestDCMod(exp, c.action)
		if got != c.want {
			t.Errorf("Underforge stacks=%d action=%s: got delta=%d want=%d", c.stacks, c.action, got, c.want)
		}
	}
}

func TestZoneConditionHarvestDCMod_DragonsLair_Awareness(t *testing.T) {
	cases := []struct {
		day  int
		want int
	}{
		{8, 0},
		{9, 4},
		{12, 4},
	}
	for _, c := range cases {
		exp := &Expedition{ZoneID: ZoneDragonsLair, CurrentDay: c.day, RegionState: map[string]any{}}
		got, _ := zoneConditionHarvestDCMod(exp, HarvestMine)
		if got != c.want {
			t.Errorf("Dragon's Lair day=%d: got delta=%d want=%d", c.day, got, c.want)
		}
	}
}

func TestZoneConditionHarvestDCMod_Abyss(t *testing.T) {
	cases := []struct {
		stack int
		want  int
	}{
		{60, 0},
		{61, 5},
		{80, 5},
		{81, 0}, // 81+ is the block branch; DC mod stops contributing.
	}
	for _, c := range cases {
		exp := &Expedition{ZoneID: ZoneAbyssPortal, TemporalStack: c.stack}
		got, _ := zoneConditionHarvestDCMod(exp, HarvestMine)
		if got != c.want {
			t.Errorf("Abyss stack=%d: got delta=%d want=%d", c.stack, got, c.want)
		}
	}
}

func TestZoneConditionHarvestDCMod_FeywildDoubleDay(t *testing.T) {
	exp := &Expedition{ZoneID: ZoneFeywildCrossing, RegionState: map[string]any{
		"feywild_today": string(FeywildDistortionDouble),
	}}
	if got, _ := zoneConditionHarvestDCMod(exp, HarvestForage); got != -3 {
		t.Errorf("Feywild double-day forage: got delta=%d want=-3", got)
	}
	if got, _ := zoneConditionHarvestDCMod(exp, HarvestMine); got != 0 {
		t.Errorf("Feywild double-day mine: should not bonus, got delta=%d", got)
	}
	exp.RegionState["feywild_today"] = string(FeywildDistortionLoop)
	if got, _ := zoneConditionHarvestDCMod(exp, HarvestForage); got != 0 {
		t.Errorf("Feywild loop should not bonus forage DC, got delta=%d", got)
	}
}

func TestRollManorCursedTrinket(t *testing.T) {
	// Wrong zone or wrong action: never fires.
	if rollManorCursedTrinket(ZoneCryptValdris, HarvestScavenge, func(int) int { return 0 }) {
		t.Errorf("non-Manor zone should never fire")
	}
	if rollManorCursedTrinket(ZoneManorBlackspire, HarvestForage, func(int) int { return 0 }) {
		t.Errorf("non-scavenge action should never fire")
	}
	// 10% threshold: roll 9 fires, roll 10 does not.
	if !rollManorCursedTrinket(ZoneManorBlackspire, HarvestScavenge, func(int) int { return 9 }) {
		t.Errorf("roll=9 should fire (under 10%%)")
	}
	if rollManorCursedTrinket(ZoneManorBlackspire, HarvestScavenge, func(int) int { return 10 }) {
		t.Errorf("roll=10 should not fire (boundary)")
	}
}

func TestRestoreHarvestNodesInRoom(t *testing.T) {
	nodeID := deriveLegacyNodeID(ZoneFeywildCrossing, 0)
	exp := &Expedition{
		ZoneID:        ZoneFeywildCrossing,
		CurrentRegion: "",
		RegionState: map[string]any{
			regionStateHarvestKey: map[string]map[string][]HarvestNode{
				"": {
					nodeID: {
						{ResourceID: "fey_dust", CurrentCharges: 0, MaxCharges: 2},
						{ResourceID: "enchanted_petal", CurrentCharges: 1, MaxCharges: 2},
					},
				},
			},
		},
	}
	if !restoreHarvestNodesInRoom(exp, nodeID) {
		t.Fatalf("restore should report changes")
	}
	table := loadHarvestTable(exp)
	got := table[""][nodeID]
	for _, n := range got {
		if n.CurrentCharges != n.MaxCharges {
			t.Errorf("node %s: charges=%d max=%d (expected restored)", n.ResourceID, n.CurrentCharges, n.MaxCharges)
		}
	}
}

func TestRestoreHarvestNodesInRoom_NoOpEmpty(t *testing.T) {
	exp := &Expedition{ZoneID: ZoneFeywildCrossing, RegionState: map[string]any{}}
	if got := restoreHarvestNodesInRoom(exp, ""); got {
		t.Errorf("empty nodeID should no-op")
	}
}

func TestIsBladeWeapon(t *testing.T) {
	cases := []struct {
		it   AdvItem
		want bool
	}{
		{AdvItem{Name: "Mithral Dagger", Type: "weapon"}, true},
		{AdvItem{Name: "Iron Longsword", Type: "weapon"}, true},
		{AdvItem{Name: "Battle Scimitar", Type: "weapon"}, true},
		{AdvItem{Name: "Heavy Mace", Type: "weapon"}, false},
		{AdvItem{Name: "Iron Dagger", Type: "consumable"}, false},
		{AdvItem{Name: "Drow Poison (diluted)", Type: "material"}, false},
	}
	for _, c := range cases {
		if got := isBladeWeapon(c.it); got != c.want {
			t.Errorf("%s (type=%s): got %v want %v", c.it.Name, c.it.Type, got, c.want)
		}
	}
}

func TestPickBladeWeapons_Coverage(t *testing.T) {
	items := []AdvItem{
		{ID: 1, Name: "Mithral Dagger", Type: "weapon"},
		{ID: 2, Name: "Iron Mace", Type: "weapon"},
		{ID: 3, Name: "Iron Longsword", Type: "weapon"},
		{ID: 4, Name: "Drow Poison (diluted)", Type: "material"},
	}
	picked, short := pickBladeWeapons(items, 2, nil)
	if short != 0 {
		t.Errorf("expected fully covered, got short=%d", short)
	}
	if len(picked) != 2 {
		t.Errorf("expected 2 picks, got %d", len(picked))
	}
	for _, id := range picked {
		if id == 2 || id == 4 {
			t.Errorf("picked non-blade id=%d", id)
		}
	}
}

func TestPickBladeWeapons_Short(t *testing.T) {
	items := []AdvItem{{ID: 1, Name: "Mithral Dagger", Type: "weapon"}}
	picked, short := pickBladeWeapons(items, 2, nil)
	if short != 1 {
		t.Errorf("expected short=1, got %d", short)
	}
	if len(picked) != 1 {
		t.Errorf("expected 1 pick, got %d", len(picked))
	}
}

func TestPickBladeWeapons_ExcludesAlreadyConsumed(t *testing.T) {
	items := []AdvItem{
		{ID: 1, Name: "Iron Dagger", Type: "weapon"},
		{ID: 2, Name: "Iron Sword", Type: "weapon"},
	}
	picked, short := pickBladeWeapons(items, 1, []int64{1})
	if short != 0 || len(picked) != 1 || picked[0] != 2 {
		t.Errorf("expected pick=[2] short=0; got pick=%v short=%d", picked, short)
	}
}

func TestThomCraftRecipes_DrowPoisonBlade_Defined(t *testing.T) {
	r, ok := findRecipeByID("drow_poison_blade")
	if !ok {
		t.Fatalf("drow_poison_blade recipe not registered")
	}
	if r.BladeWeaponCount != 1 {
		t.Errorf("expected BladeWeaponCount=1, got %d", r.BladeWeaponCount)
	}
	if _, ok := r.Ingredients["Drow Poison (diluted)"]; !ok {
		t.Errorf("expected Drow Poison ingredient")
	}
	if r.OutputType != "weapon" {
		t.Errorf("expected weapon output type, got %s", r.OutputType)
	}
}
