package appupdate

import "testing"

func policy() Policy {
	return Policy{
		Enabled:        true,
		LatestVersion:  "0.1.54",
		MinimumVersion: "0.1.50",
		DownloadURL:    "https://nas.example.com/memby/memby-0.1.54.apk",
		SHA256:         "abcdef",
		SizeBytes:      42,
		Notes:          "Faster home screen",
	}
}

func TestUpToDateClientIsLeftAlone(t *testing.T) {
	for _, version := range []string{"0.1.54", "0.1.55", "0.2.0", "1.0.0"} {
		if got := Decide(policy(), version).Status; got != StatusNone {
			t.Fatalf("client %s: got %s, want none", version, got)
		}
	}
}

func TestBehindLatestIsOptional(t *testing.T) {
	decision := Decide(policy(), "0.1.53")

	if decision.Status != StatusOptional {
		t.Fatalf("got %s, want optional", decision.Status)
	}
	if decision.Version != "0.1.54" || decision.DownloadURL == "" {
		t.Fatalf("decision is missing what the TV needs to act: %+v", decision)
	}
	if decision.SHA256 != "abcdef" || decision.SizeBytes != 42 {
		t.Fatalf("decision dropped release integrity metadata: %+v", decision)
	}
}

func TestBelowMinimumIsMandatory(t *testing.T) {
	if got := Decide(policy(), "0.1.49").Status; got != StatusMandatory {
		t.Fatalf("got %s, want mandatory", got)
	}
}

// Setting minimum = latest is how an operator forces everyone onto the current build.
func TestMinimumEqualToLatestForcesEveryOldClient(t *testing.T) {
	p := policy()
	p.MinimumVersion = p.LatestVersion

	if got := Decide(p, "0.1.53").Status; got != StatusMandatory {
		t.Fatalf("got %s, want mandatory", got)
	}
	if got := Decide(p, "0.1.54").Status; got != StatusNone {
		t.Fatalf("a current client should still be left alone, got %s", got)
	}
}

func TestBlankMinimumKeepsUpdatesOptional(t *testing.T) {
	p := policy()
	p.MinimumVersion = ""

	if got := Decide(p, "0.0.1").Status; got != StatusOptional {
		t.Fatalf("got %s, want optional", got)
	}
}

func TestDisabledPolicySaysNothing(t *testing.T) {
	p := policy()
	p.Enabled = false

	if got := Decide(p, "0.0.1").Status; got != StatusNone {
		t.Fatalf("got %s, want none", got)
	}
}

func TestPolicyWithNoLatestVersionSaysNothing(t *testing.T) {
	p := policy()
	p.LatestVersion = "  "

	if got := Decide(p, "0.0.1").Status; got != StatusNone {
		t.Fatalf("got %s, want none", got)
	}
}

// A client that cannot report its version must not be hard-blocked: it would be stuck
// behind a prompt it may have no way to satisfy.
func TestUnknownClientVersionIsNeverForced(t *testing.T) {
	p := policy()
	p.MinimumVersion = p.LatestVersion

	for _, version := range []string{"", "   ", "?", "unknown"} {
		if got := Decide(p, version).Status; got != StatusNone {
			t.Fatalf("client %q: got %s, want none", version, got)
		}
	}
}

func TestVersionParsingIsForgiving(t *testing.T) {
	p := policy()

	// Leading "v", pre-release suffixes and short versions all compare sensibly.
	if got := Decide(p, "v0.1.53").Status; got != StatusOptional {
		t.Fatalf("v-prefixed: got %s, want optional", got)
	}
	if got := Decide(p, "0.1.54-beta").Status; got != StatusNone {
		t.Fatalf("pre-release suffix should compare as 0.1.54, got %s", got)
	}
	p.LatestVersion = "0.2"
	if got := Decide(p, "0.2.0").Status; got != StatusNone {
		t.Fatalf("0.2.0 should equal 0.2, got %s", got)
	}
}

func TestMandatoryOutranksOptional(t *testing.T) {
	p := policy()
	p.MinimumVersion = "0.1.53"

	// Below both thresholds: the stronger verdict must win.
	if got := Decide(p, "0.1.52").Status; got != StatusMandatory {
		t.Fatalf("got %s, want mandatory", got)
	}
}
