package actions

import (
	"encoding/json"
	"testing"
)

func TestActionArgString(t *testing.T) {
	tests := []struct {
		name  string
		input string
	}{
		{"empty string", ""},
		{"simple string", "hello"},
		{"special chars", "special: !@#$%^&*()"},
		{"unicode", "unicode: 你好世界"},
		{"newline", "line1\nline2"},
		{"json", `{"key": "value"}`},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			arg := ActionArgString(tt.input)
			if got := arg.String(); got != tt.input {
				t.Errorf("ActionArgString(%q).String() = %q, want %q", tt.input, got, tt.input)
			}

			var newArg actionArgString
			if err := newArg.FromString(tt.input); err != nil {
				t.Errorf("actionArgString.FromString(%q) error = %v", tt.input, err)
			}
			if got := newArg.String(); got != tt.input {
				t.Errorf("FromString(%q).String() = %q, want %q", tt.input, got, tt.input)
			}
		})
	}
}

func TestActionArgBool(t *testing.T) {
	arg := ActionArgBool(true)
	if got := arg.String(); got != "true" {
		t.Errorf("ActionArgBool(true).String() = %q, want true", got)
	}

	var parsed actionArgBool
	if err := parsed.FromString("false"); err != nil {
		t.Fatalf("actionArgBool.FromString(false) error = %v", err)
	}
	if bool(parsed) {
		t.Error("FromString(false) = true, want false")
	}
}

func TestActionArgBoolFromStringErrors(t *testing.T) {
	var arg actionArgBool
	if err := arg.FromString("not-a-bool"); err == nil {
		t.Error("FromString(not-a-bool) should return error, got nil")
	}
}

func TestBuildPBFromActionArgsSerializesBoolAsString(t *testing.T) {
	args := ActionArgs{
		"enabled": ActionArgBool(true),
	}

	pbArgs, err := BuildPBFromActionArgs(args)
	if err != nil {
		t.Fatalf("BuildPBFromActionArgs() error = %v", err)
	}
	if got := pbArgs["enabled"].GetActionArgString(); got != "true" {
		t.Errorf("bool action arg serialized as %q, want true", got)
	}
}

func TestActionArgInt(t *testing.T) {
	tests := []struct {
		name  string
		input int
	}{
		{"zero", 0},
		{"positive", 123},
		{"negative", -456},
		{"large", 999999999},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			arg := ActionArgInt(tt.input)
			if got := arg.String(); got == "" {
				t.Errorf("ActionArgInt(%d).String() returned empty", tt.input)
			}

			var newArg actionArgInt
			if err := newArg.FromString("789"); err != nil {
				t.Errorf("actionArgInt.FromString() error = %v", err)
			}
			if int(newArg) != 789 {
				t.Errorf("FromString(\"789\") = %d, want 789", int(newArg))
			}
		})
	}
}

func TestActionArgIntFromStringErrors(t *testing.T) {
	var arg actionArgInt
	err := arg.FromString("not-a-number")
	if err == nil {
		t.Error("FromString(\"not-a-number\") should return error, got nil")
	}
}

func TestActionArgsId(t *testing.T) {
	t.Run("empty args returns empty id", func(t *testing.T) {
		args := ActionArgs{}
		if got := args.Id(); got != "" {
			t.Errorf("empty ActionArgs.Id() = %q, want empty", got)
		}
	})

	t.Run("single arg produces consistent id", func(t *testing.T) {
		args := ActionArgs{
			"key": ActionArgString("value"),
		}
		id1 := args.Id()
		id2 := args.Id()
		if id1 != id2 {
			t.Errorf("same args produced different ids: %q vs %q", id1, id2)
		}
	})

	t.Run("multiple args order independence", func(t *testing.T) {
		args1 := ActionArgs{
			"a": ActionArgString("1"),
			"b": ActionArgInt(2),
		}
		args2 := ActionArgs{
			"b": ActionArgInt(2),
			"a": ActionArgString("1"),
		}
		id1 := args1.Id()
		id2 := args2.Id()
		if id1 != id2 {
			t.Errorf("different order produced different ids: %q vs %q", id1, id2)
		}
	})

	t.Run("different values produce different ids", func(t *testing.T) {
		args1 := ActionArgs{"key": ActionArgString("value1")}
		args2 := ActionArgs{"key": ActionArgString("value2")}
		if args1.Id() == args2.Id() {
			t.Error("different values produced same id")
		}
	})
}

func TestActionArgsGetString(t *testing.T) {
	t.Run("existing key", func(t *testing.T) {
		args := ActionArgs{
			"key": ActionArgString("value"),
		}
		got, err := args.GetString("key")
		if err != nil {
			t.Errorf("GetString(%q) error = %v", "key", err)
		}
		if got != "value" {
			t.Errorf("GetString(%q) = %q, want %q", "key", got, "value")
		}
	})

	t.Run("missing key", func(t *testing.T) {
		args := ActionArgs{}
		_, err := args.GetString("missing")
		if err == nil {
			t.Error("GetString(\"missing\") should return error, got nil")
		}
	})
}

func TestActionArgsGetBool(t *testing.T) {
	t.Run("existing true", func(t *testing.T) {
		args := ActionArgs{"flag": ActionArgBool(true)}
		got, err := args.GetBool("flag")
		if err != nil {
			t.Errorf("GetBool(flag) error = %v", err)
		}
		if !got {
			t.Error("GetBool(flag) = false, want true")
		}
	})

	t.Run("existing false", func(t *testing.T) {
		args := ActionArgs{"flag": ActionArgBool(false)}
		got, err := args.GetBool("flag")
		if err != nil {
			t.Errorf("GetBool(flag) error = %v", err)
		}
		if got {
			t.Error("GetBool(flag) = true, want false")
		}
	})

	t.Run("missing key", func(t *testing.T) {
		args := ActionArgs{}
		if _, err := args.GetBool("missing"); err == nil {
			t.Error("GetBool(missing) should return error, got nil")
		}
	})
}

func TestActionArgsClone(t *testing.T) {
	original := ActionArgs{
		"str": ActionArgString("value"),
		"int": ActionArgInt(123),
	}

	cloned := original.Clone()

	if len(cloned) != len(original) {
		t.Errorf("Clone() len = %d, want %d", len(cloned), len(original))
	}

	for k, v := range original {
		cv, ok := cloned[k]
		if !ok {
			t.Errorf("Clone() missing key %q", k)
		}
		if cv.String() != v.String() {
			t.Errorf("Clone()[%q] = %v, want %v", k, cv, v)
		}
	}
}

func TestActionArgsUnmarshalJSON(t *testing.T) {
	t.Run("string values", func(t *testing.T) {
		jsonData := `{"key1": "value1", "key2": "value2"}`
		var args ActionArgs
		if err := json.Unmarshal([]byte(jsonData), &args); err != nil {
			t.Fatalf("UnmarshalJSON() error = %v", err)
		}

		if len(args) != 2 {
			t.Errorf("args len = %d, want 2", len(args))
		}
		if args["key1"] == nil {
			t.Error("args[\"key1\"] is nil")
		}
		if args["key2"] == nil {
			t.Error("args[\"key2\"] is nil")
		}
	})

	t.Run("integer values", func(t *testing.T) {
		jsonData := `{"count": "123"}`
		var args ActionArgs
		if err := json.Unmarshal([]byte(jsonData), &args); err != nil {
			t.Fatalf("UnmarshalJSON() error = %v", err)
		}

		arg := args["count"]
		if arg == nil {
			t.Fatal("args[\"count\"] is nil")
		}
		if arg.String() != "123" {
			t.Errorf("args[\"count\"].String() = %q, want %q", arg.String(), "123")
		}
	})

	t.Run("empty object", func(t *testing.T) {
		jsonData := `{}`
		var args ActionArgs
		if err := json.Unmarshal([]byte(jsonData), &args); err != nil {
			t.Fatalf("UnmarshalJSON() error = %v", err)
		}
		if len(args) != 0 {
			t.Errorf("UnmarshalJSON({}) len = %d, want 0", len(args))
		}
	})

	t.Run("invalid json", func(t *testing.T) {
		jsonData := `{invalid}`
		var args ActionArgs
		if err := json.Unmarshal([]byte(jsonData), &args); err == nil {
			t.Error("UnmarshalJSON() should return error for invalid json")
		}
	})
}

func TestMergeActionArgs(t *testing.T) {
	t.Run("merge with empty", func(t *testing.T) {
		a := ActionArgs{"key": ActionArgString("value")}
		b := ActionArgs{}
		merged := MergeActionArgs(a, b)

		if got, _ := merged.GetString("key"); got != "value" {
			t.Errorf("merged[\"key\"] = %q, want %q", got, "value")
		}
	})

	t.Run("merge with new keys", func(t *testing.T) {
		a := ActionArgs{"a": ActionArgString("1")}
		b := ActionArgs{"b": ActionArgString("2")}
		merged := MergeActionArgs(a, b)

		if len(merged) != 2 {
			t.Errorf("merged len = %d, want 2", len(merged))
		}
		if got, _ := merged.GetString("a"); got != "1" {
			t.Errorf("merged[\"a\"] = %q, want 1", got)
		}
		if got, _ := merged.GetString("b"); got != "2" {
			t.Errorf("merged[\"b\"] = %q, want 2", got)
		}
	})

	t.Run("merge overrides", func(t *testing.T) {
		a := ActionArgs{"key": ActionArgString("old")}
		b := ActionArgs{"key": ActionArgString("new")}
		merged := MergeActionArgs(a, b)

		if got, _ := merged.GetString("key"); got != "new" {
			t.Errorf("merged[\"key\"] = %q, want %q (b should override)", got, "new")
		}
	})

	t.Run("original not modified", func(t *testing.T) {
		a := ActionArgs{"key": ActionArgString("value")}
		b := ActionArgs{"new": ActionArgString("added")}
		MergeActionArgs(a, b)

		if _, ok := a["new"]; ok {
			t.Error("original 'a' was modified by merge")
		}
	})
}
