package dayzxml

import (
	"strings"
	"testing"
)

func TestDetectKind(t *testing.T) {
	cases := []struct {
		in   string
		want FileKind
	}{
		{`<types><type name="X"/></types>`, KindTypes},
		{`<events><event name="X"/></events>`, KindEvents},
		{`<spawnabletypes><type name="X"/></spawnabletypes>`, KindSpawnableTypes},
		{`<messages><message/></messages>`, KindMessages},
		{`<randompresets><cargo name="X"/></randompresets>`, KindRandomPresets},
		{`<eventposdef><event name="X"/></eventposdef>`, KindEventSpawns},
	}
	for _, c := range cases {
		got, err := DetectKind([]byte(c.in))
		if err != nil {
			t.Errorf("DetectKind(%q) err: %v", c.in, err)
			continue
		}
		if got != c.want {
			t.Errorf("DetectKind(%q) = %v, want %v", c.in, got, c.want)
		}
	}
	if _, err := DetectKind([]byte(`<foo/>`)); err == nil {
		t.Error("expected error for unknown root")
	}
}

func TestMergeTypes_AddAndReplace(t *testing.T) {
	base := []byte(`<types>
    <type name="AKM"><nominal>5</nominal><min>2</min></type>
    <type name="M4A1"><nominal>3</nominal><min>1</min></type>
</types>`)
	snippet := []byte(`<types>
    <type name="AKM"><nominal>8</nominal><min>4</min></type>
    <type name="NewItem"><nominal>1</nominal><min>1</min></type>
</types>`)
	res, err := Merge(KindTypes, base, snippet, false)
	if err != nil {
		t.Fatal(err)
	}
	if len(res.Plan.Added) != 1 || res.Plan.Added[0] != "NewItem" {
		t.Errorf("Added = %v, want [NewItem]", res.Plan.Added)
	}
	if len(res.Plan.Replaced) != 1 || res.Plan.Replaced[0] != "AKM" {
		t.Errorf("Replaced = %v, want [AKM]", res.Plan.Replaced)
	}
	out := string(res.Bytes)
	if !strings.Contains(out, `name="NewItem"`) {
		t.Error("merged output missing NewItem")
	}
	if !strings.Contains(out, `<nominal>8</nominal>`) {
		t.Error("merged output missing updated AKM nominal value")
	}
	if !strings.Contains(out, `name="M4A1"`) {
		t.Error("merged output dropped untouched M4A1 entry")
	}
}

func TestMergeTypes_Unchanged(t *testing.T) {
	xml := []byte(`<types>
    <type name="AKM"><nominal>5</nominal></type>
</types>`)
	res, err := Merge(KindTypes, xml, xml, true)
	if err != nil {
		t.Fatal(err)
	}
	if len(res.Plan.Unchanged) != 1 || res.Plan.Unchanged[0] != "AKM" {
		t.Errorf("Unchanged = %v, want [AKM]", res.Plan.Unchanged)
	}
	if len(res.Plan.Replaced) != 0 {
		t.Errorf("Replaced = %v, want []", res.Plan.Replaced)
	}
}

func TestMergeEvents_AppendsChildren(t *testing.T) {
	base := []byte(`<events>
    <event name="VehicleSpawn">
        <nominal>3</nominal>
        <children>
            <child type="UAZ_Base" min="1" max="1"/>
        </children>
    </event>
</events>`)
	snippet := []byte(`<events>
    <event name="VehicleSpawn">
        <nominal>5</nominal>
        <children>
            <child type="UAZ_Mod1" min="1" max="1"/>
            <child type="UAZ_Base" min="1" max="1"/>
        </children>
    </event>
</events>`)
	res, err := Merge(KindEvents, base, snippet, false)
	if err != nil {
		t.Fatal(err)
	}
	out := string(res.Bytes)
	// nominal overridden
	if !strings.Contains(out, `<nominal>5</nominal>`) {
		t.Error("event scalar child <nominal> not overridden")
	}
	// new child appended, existing child not duplicated
	if strings.Count(out, `type="UAZ_Base"`) != 1 {
		t.Errorf("existing child duplicated; output:\n%s", out)
	}
	if !strings.Contains(out, `type="UAZ_Mod1"`) {
		t.Error("new child not appended")
	}
	if got := res.Plan.AppendedChildren["VehicleSpawn"]; len(got) != 1 || got[0] != "UAZ_Mod1" {
		t.Errorf("AppendedChildren[VehicleSpawn] = %v, want [UAZ_Mod1]", got)
	}
}

func TestMergeMessages_AppendOnly(t *testing.T) {
	base := []byte(`<messages>
    <message><id>1</id><text>Welcome</text></message>
</messages>`)
	snippet := []byte(`<messages>
    <message><id>2</id><text>Restart in 10m</text></message>
</messages>`)
	res, err := Merge(KindMessages, base, snippet, false)
	if err != nil {
		t.Fatal(err)
	}
	if len(res.Plan.Added) != 1 {
		t.Errorf("Added count = %d, want 1", len(res.Plan.Added))
	}
	if !strings.Contains(string(res.Bytes), "Restart in 10m") {
		t.Error("appended message not present")
	}
}

func TestStripBOM(t *testing.T) {
	bom := []byte{0xEF, 0xBB, 0xBF}
	raw := append(bom, []byte(`<types><type name="X"/></types>`)...)
	k, err := DetectKind(raw)
	if err != nil {
		t.Fatal(err)
	}
	if k != KindTypes {
		t.Errorf("kind = %v, want %v", k, KindTypes)
	}
}

func TestRootMismatch(t *testing.T) {
	base := []byte(`<types></types>`)
	snippet := []byte(`<events></events>`)
	if _, err := Merge(KindTypes, base, snippet, true); err == nil {
		t.Error("expected error when snippet root doesn't match")
	}
}

func TestPreserveCRLF(t *testing.T) {
	base := []byte("<types>\r\n    <type name=\"AKM\"><nominal>5</nominal></type>\r\n</types>")
	snippet := []byte(`<types><type name="NEW"><nominal>1</nominal></type></types>`)
	res, err := Merge(KindTypes, base, snippet, false)
	if err != nil {
		t.Fatal(err)
	}
	if !strings.Contains(string(res.Bytes), "\r\n") {
		t.Error("output dropped CRLF line endings; base had CRLF")
	}
}
