package setupcmd

import (
	"bytes"
	"context"
	"errors"
	"math"
	"strings"
	"testing"

	"github.com/nexuscrm/persistence"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionstatev1"
)

func TestParsePermissionStateActivationArgsRequiresExactMaintenanceFence(t *testing.T) {
	lineage := "0102030405060708090a0b0c0d0e0f10"
	args, err := parsePermissionStateActivationArgs([]string{
		"--expected-lineage", lineage,
		"--expected-revision", "27",
		"--confirm-maintenance-window",
	}, &bytes.Buffer{})
	if err != nil {
		t.Fatal(err)
	}
	if got := args.expected.Revision(); got != 27 {
		t.Fatalf("expected revision = %d, want 27", got)
	}
	if got := permissionStateLineageHex(args.expected.Lineage()); got != lineage {
		t.Fatalf("expected lineage = %s, want %s", got, lineage)
	}

	tests := []struct {
		name string
		args []string
		want string
	}{
		{
			name: "maintenance confirmation",
			args: []string{"--expected-lineage", lineage, "--expected-revision", "27"},
			want: "confirm-maintenance-window",
		},
		{
			name: "lineage",
			args: []string{"--expected-lineage", "abcd", "--expected-revision", "27", "--confirm-maintenance-window"},
			want: "32 hexadecimal",
		},
		{
			name: "zero lineage",
			args: []string{"--expected-lineage", strings.Repeat("0", 32), "--expected-revision", "27", "--confirm-maintenance-window"},
			want: "nonzero",
		},
		{
			name: "revision",
			args: []string{"--expected-lineage", lineage, "--expected-revision", "0", "--confirm-maintenance-window"},
			want: "positive decimal",
		},
		{
			name: "overflow",
			args: []string{"--expected-lineage", lineage, "--expected-revision", "18446744073709551615", "--confirm-maintenance-window"},
			want: "successor",
		},
		{
			name: "unexpected argument",
			args: []string{"--expected-lineage", lineage, "--expected-revision", "27", "--confirm-maintenance-window", "extra"},
			want: "unexpected arguments",
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			_, err := parsePermissionStateActivationArgs(test.args, &bytes.Buffer{})
			if err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("parse error = %v, want containing %q", err, test.want)
			}
		})
	}
}

func TestRunPermissionStateActivationCommandPromotesExactPredecessor(t *testing.T) {
	expected := permissionStateActivationVersionForTest(t, 27)
	successor := permissionStateActivationVersionForTest(t, 28)
	activator := &permissionStateActivationFake{
		state: persistence.PermissionState{
			Version:         successor,
			EnforcementMode: persistence.PermissionStateEnforcedMode,
		},
	}
	var stdout, stderr bytes.Buffer
	code := runPermissionStateActivationCommand(
		context.Background(),
		[]string{
			"--expected-lineage", permissionStateLineageHex(expected.Lineage()),
			"--expected-revision", "27",
			"--confirm-maintenance-window",
		},
		&stdout,
		&stderr,
		func() (permissionStateActivator, error) { return activator, nil },
	)
	if code != 0 {
		t.Fatalf("exit = %d, stderr = %q", code, stderr.String())
	}
	if activator.calls != 1 {
		t.Fatalf("activation calls = %d, want 1", activator.calls)
	}
	if comparison, err := activator.expected.Compare(expected); err != nil || comparison != 0 {
		t.Fatalf("activation expected version mismatch: comparison=%d err=%v", comparison, err)
	}
	want := `{"lineage":"0102030405060708090a0b0c0d0e0f10","revision":28,"enforcementMode":"enforced"}` + "\n"
	if stdout.String() != want {
		t.Fatalf("stdout = %q, want %q", stdout.String(), want)
	}
	if stderr.Len() != 0 {
		t.Fatalf("stderr = %q, want empty", stderr.String())
	}
}

func TestRunPermissionStateActivationCommandFailsClosed(t *testing.T) {
	expected := permissionStateActivationVersionForTest(t, 27)
	args := []string{
		"--expected-lineage", permissionStateLineageHex(expected.Lineage()),
		"--expected-revision", "27",
		"--confirm-maintenance-window",
	}
	t.Run("parse before repository", func(t *testing.T) {
		called := false
		var stdout, stderr bytes.Buffer
		code := runPermissionStateActivationCommand(
			context.Background(),
			nil,
			&stdout,
			&stderr,
			func() (permissionStateActivator, error) {
				called = true
				return nil, nil
			},
		)
		if code != 2 || called || stdout.Len() != 0 {
			t.Fatalf("parse failure = code %d, factory %t, stdout %q", code, called, stdout.String())
		}
	})
	t.Run("repository", func(t *testing.T) {
		var stdout, stderr bytes.Buffer
		code := runPermissionStateActivationCommand(
			context.Background(),
			args,
			&stdout,
			&stderr,
			func() (permissionStateActivator, error) { return nil, errors.New("unavailable") },
		)
		if code != 1 || !strings.Contains(stderr.String(), "open repository") || stdout.Len() != 0 {
			t.Fatalf("repository failure = code %d, stderr %q, stdout %q", code, stderr.String(), stdout.String())
		}
	})
	t.Run("compare and set", func(t *testing.T) {
		var stdout, stderr bytes.Buffer
		code := runPermissionStateActivationCommand(
			context.Background(),
			args,
			&stdout,
			&stderr,
			func() (permissionStateActivator, error) {
				return &permissionStateActivationFake{err: errors.New("conflict")}, nil
			},
		)
		if code != 1 || !strings.Contains(stderr.String(), "activate permission state") || stdout.Len() != 0 {
			t.Fatalf("activation failure = code %d, stderr %q, stdout %q", code, stderr.String(), stdout.String())
		}
	})
	t.Run("inexact result", func(t *testing.T) {
		var stdout, stderr bytes.Buffer
		code := runPermissionStateActivationCommand(
			context.Background(),
			args,
			&stdout,
			&stderr,
			func() (permissionStateActivator, error) {
				return &permissionStateActivationFake{state: persistence.PermissionState{
					Version:         permissionStateActivationVersionForTest(t, 29),
					EnforcementMode: persistence.PermissionStateEnforcedMode,
				}}, nil
			},
		)
		if code != 1 || !strings.Contains(stderr.String(), "exact successor") || stdout.Len() != 0 {
			t.Fatalf("inexact result = code %d, stderr %q, stdout %q", code, stderr.String(), stdout.String())
		}
	})
}

type permissionStateActivationFake struct {
	state    persistence.PermissionState
	err      error
	calls    int
	expected permissionstatev1.Version
}

func (f *permissionStateActivationFake) PromoteToEnforced(
	_ context.Context,
	expected permissionstatev1.Version,
) (persistence.PermissionState, error) {
	f.calls++
	f.expected = expected
	return f.state, f.err
}

func permissionStateActivationVersionForTest(t *testing.T, revision uint64) permissionstatev1.Version {
	t.Helper()
	if revision == math.MaxUint64 {
		t.Fatal("test revision must have a successor")
	}
	lineage, err := permissionstatev1.LineageFromBytes([]byte{
		1, 2, 3, 4, 5, 6, 7, 8,
		9, 10, 11, 12, 13, 14, 15, 16,
	})
	if err != nil {
		t.Fatal(err)
	}
	version, err := permissionstatev1.NewVersion(lineage, revision)
	if err != nil {
		t.Fatal(err)
	}
	return version
}
