package firecracker

import (
	"strings"
	"testing"
)

func TestParseVersionOutput(t *testing.T) {
	t.Parallel()
	for _, tc := range []struct {
		name    string
		input   string
		want    SemVer
		wantErr bool
	}{
		{name: "canonical", input: "Firecracker v1.14.1\n", want: SemVer{Major: 1, Minor: 14, Patch: 1}},
		{name: "with_trailing_log", input: "Firecracker v1.14.1\n\n2026-04-28T17:38:12.392171332 [anonymous-instance:main] exit_code=0\n", want: SemVer{Major: 1, Minor: 14, Patch: 1}},
		{name: "prerelease", input: "Firecracker v1.10.0-rc1\n", want: SemVer{Major: 1, Minor: 10, Patch: 0, PreRelease: "rc1"}},
		{name: "two_digit_minor", input: "Firecracker v2.0.42\n", want: SemVer{Major: 2, Minor: 0, Patch: 42}},
		{name: "garbage", input: "not a firecracker", wantErr: true},
		{name: "empty", input: "", wantErr: true},
		{name: "missing_v", input: "Firecracker 1.14.1", wantErr: true},
	} {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			got, err := ParseVersionOutput(tc.input)
			if tc.wantErr {
				if err == nil {
					t.Fatalf("ParseVersionOutput(%q) succeeded, want error", tc.input)
				}
				return
			}
			if err != nil {
				t.Fatalf("ParseVersionOutput(%q) = %v", tc.input, err)
			}
			if got != tc.want {
				t.Fatalf("ParseVersionOutput(%q) = %+v, want %+v", tc.input, got, tc.want)
			}
		})
	}
}

func TestSemVerCompare(t *testing.T) {
	t.Parallel()
	for _, tc := range []struct {
		name string
		a, b string
		want int
	}{
		{name: "equal", a: "1.14.1", b: "1.14.1", want: 0},
		{name: "patch_lower", a: "1.14.0", b: "1.14.1", want: -1},
		{name: "patch_higher", a: "1.14.2", b: "1.14.1", want: 1},
		{name: "minor_dominates_patch", a: "1.10.999", b: "1.11.0", want: -1},
		{name: "major_dominates", a: "2.0.0", b: "1.99.99", want: 1},
		{name: "min_vs_tested_today", a: MinSupportedVersion, b: KnownTestedVersion, want: -1},
	} {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			a := MustParseSemVer(tc.a)
			b := MustParseSemVer(tc.b)
			if got := a.Compare(b); got != tc.want {
				t.Fatalf("(%s).Compare(%s) = %d, want %d", tc.a, tc.b, got, tc.want)
			}
		})
	}
}

func TestSemVerString(t *testing.T) {
	t.Parallel()
	if got := MustParseSemVer("1.14.1").String(); got != "v1.14.1" {
		t.Fatalf("v1.14.1.String() = %q", got)
	}
	pre := SemVer{Major: 1, Minor: 10, Patch: 0, PreRelease: "rc1"}
	if got := pre.String(); got != "v1.10.0-rc1" {
		t.Fatalf("rc String() = %q", got)
	}
}

// MustParseSemVer panics on malformed input; pin a few inputs so a
// future refactor doesn't accidentally widen what counts as valid.
func TestMustParseSemVerRejectsMalformed(t *testing.T) {
	t.Parallel()
	for _, bad := range []string{"", "1", "1.2", "1.2.3.4", "v1.2.x", "vfoo"} {
		bad := bad
		t.Run(strings.ReplaceAll(bad, ".", "_"), func(t *testing.T) {
			defer func() {
				if r := recover(); r == nil {
					t.Errorf("MustParseSemVer(%q) did not panic", bad)
				}
			}()
			_ = MustParseSemVer(bad)
		})
	}
}
