package runtimeassembly

import (
	"context"
	"errors"
	"testing"

	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/models"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionstatev1"
)

type effectivePermissionReaderFunc func(
	context.Context,
	*models.UserSession,
	string,
) (*contract.EffectivePermissionsResult, error)

func (f effectivePermissionReaderFunc) EffectivePermissions(
	ctx context.Context,
	viewer *models.UserSession,
	target string,
) (*contract.EffectivePermissionsResult, error) {
	return f(ctx, viewer, target)
}

type permissionSnapshotTransactionsFunc func(
	context.Context,
	func(context.Context) error,
) error

func (f permissionSnapshotTransactionsFunc) WithTransaction(
	ctx context.Context,
	run func(context.Context) error,
) error {
	return f(ctx, run)
}

func (permissionSnapshotTransactionsFunc) IsTransactionActive(ctx context.Context) bool {
	bound, _ := ctx.Value(permissionSnapshotContextKey{}).(bool)
	return bound
}

type permissionStateSourceFunc func(context.Context) (persistence.PermissionState, error)

func (f permissionStateSourceFunc) LoadCurrent(
	ctx context.Context,
) (persistence.PermissionState, error) {
	return f(ctx)
}

func (f permissionStateSourceFunc) LockCurrent(
	ctx context.Context,
) (persistence.PermissionState, error) {
	return f(ctx)
}

type effectiveObjectPermissionCheckerFunc func(
	context.Context,
	string,
	string,
	*models.UserSession,
) bool

func (f effectiveObjectPermissionCheckerFunc) CheckObjectPermissionWithUser(
	ctx context.Context,
	objectAPIName string,
	operation string,
	user *models.UserSession,
) bool {
	return f(ctx, objectAPIName, operation, user)
}

func (effectiveObjectPermissionCheckerFunc) CheckFieldVisibilityWithUser(
	context.Context,
	string,
	string,
	*models.UserSession,
) (bool, error) {
	return true, nil
}

func allowEffectiveObjectPermission(
	context.Context,
	string,
	string,
	*models.UserSession,
) bool {
	return true
}

type permissionSnapshotContextKey struct{}

type effectiveActionPermissionOwner struct {
	t             *testing.T
	version       permissionstatev1.Version
	profileID     string
	system        []string
	custom        []string
	snapshotReads int
}

func (owner *effectiveActionPermissionOwner) CheckObjectPermissionWithUser(
	context.Context, string, string, *models.UserSession,
) bool {
	return true
}

func (owner *effectiveActionPermissionOwner) CheckFieldVisibilityWithUser(
	context.Context, string, string, *models.UserSession,
) (bool, error) {
	return true, nil
}

func (owner *effectiveActionPermissionOwner) LoadCurrentEffectivePermissionSnapshot(
	ctx context.Context,
	subject permissionstatev1.Subject,
) (permissionstatev1.EffectiveSnapshot, error) {
	owner.t.Helper()
	owner.requireBound(ctx)
	if subject.UserID() != "viewer" || subject.ProfileID() != owner.profileID {
		owner.t.Fatalf(
			"action permission authority = %q/%q",
			subject.UserID(), subject.ProfileID(),
		)
	}
	owner.snapshotReads++
	return permissionstatev1.NewEffectiveSnapshot(
		owner.version,
		owner.system,
		owner.custom,
	)
}

func (owner *effectiveActionPermissionOwner) requireBound(ctx context.Context) {
	owner.t.Helper()
	if bound, _ := ctx.Value(permissionSnapshotContextKey{}).(bool); !bound {
		owner.t.Fatal("action permission read escaped the owned snapshot")
	}
}

func TestEffectiveActionPermissionsReadOnlyActionFactsAndReuseOwnedTransaction(t *testing.T) {
	floor := permissionProjectionVersion(t, 17)
	completeReads := 0
	transactionsStarted := 0
	owner := &effectiveActionPermissionOwner{
		t: t, version: floor, profileID: "current-profile",
		system: []string{"ConvertLeads"},
	}
	projection, err := newEffectivePermissionProjection(
		effectivePermissionReaderFunc(func(
			context.Context, *models.UserSession, string,
		) (*contract.EffectivePermissionsResult, error) {
			completeReads++
			return nil, errors.New("complete permission export must not run")
		}),
		permissionSnapshotTransactionsFunc(func(
			ctx context.Context, run func(context.Context) error,
		) error {
			transactionsStarted++
			return run(context.WithValue(ctx, permissionSnapshotContextKey{}, true))
		}),
		permissionStateSourceFunc(func(ctx context.Context) (persistence.PermissionState, error) {
			owner.requireBound(ctx)
			return persistence.PermissionState{
				Version: floor, EnforcementMode: persistence.PermissionStateEnforcedMode,
			}, nil
		}),
		owner,
	)
	if err != nil {
		t.Fatal(err)
	}
	viewer := &models.UserSession{ID: "viewer", ProfileID: "current-profile"}
	got, err := projection.EffectiveActionPermissions(t.Context(), viewer, floor)
	if err != nil || got == nil || !got.SystemPermissions["ConvertLeads"] ||
		got.CustomPermissions["SkipOpportunityOnConvert"] {
		t.Fatalf("action permissions = %#v, %v", got, err)
	}
	if completeReads != 0 || transactionsStarted != 1 || owner.snapshotReads != 1 {
		t.Fatalf(
			"preflight reads complete=%d tx=%d snapshot=%d",
			completeReads, transactionsStarted, owner.snapshotReads,
		)
	}
	got.SystemPermissions["ConvertLeads"] = false
	if owner.system[0] != "ConvertLeads" {
		t.Fatal("returned action permissions mutated the permission snapshot")
	}
	got, err = projection.EffectiveActionPermissionsWithinTransaction(
		context.WithValue(t.Context(), permissionSnapshotContextKey{}, true),
		viewer, floor,
	)
	if err != nil || got == nil || !got.SystemPermissions["ConvertLeads"] ||
		completeReads != 0 || transactionsStarted != 1 || owner.snapshotReads != 2 {
		t.Fatalf(
			"transaction action permissions=%#v err=%v complete=%d tx=%d snapshot=%d",
			got, err, completeReads, transactionsStarted, owner.snapshotReads,
		)
	}
	owner.version = permissionProjectionVersion(t, 18)
	_, err = projection.EffectiveActionPermissionsWithinTransaction(
		context.WithValue(t.Context(), permissionSnapshotContextKey{}, true),
		viewer, floor,
	)
	var failure *permissionsemantic.Failure
	if !errors.As(err, &failure) ||
		failure.Category != permissionsemantic.FailureUnavailable ||
		owner.snapshotReads != 3 {
		t.Fatalf("changed action snapshot = %v; reads=%d", err, owner.snapshotReads)
	}
	_, err = projection.EffectiveActionPermissionsWithinTransaction(
		context.WithValue(t.Context(), permissionSnapshotContextKey{}, true),
		&models.UserSession{ID: "viewer"}, floor,
	)
	if !errors.As(err, &failure) ||
		failure.Category != permissionsemantic.FailureUnavailable ||
		owner.snapshotReads != 3 {
		t.Fatalf("missing action subject = %v; reads=%d", err, owner.snapshotReads)
	}
}

func TestEffectivePermissionProjectionBindsCompleteReadToAdmittedFloor(t *testing.T) {
	floor := permissionProjectionVersion(t, 7)
	viewer := &models.UserSession{ID: "viewer", ProfileID: "profile"}
	want := &contract.EffectivePermissionsResult{
		SystemPermissions: map[string]bool{"ViewSetup": true},
	}
	readerCalled := false
	projection, err := newEffectivePermissionProjection(
		effectivePermissionReaderFunc(func(
			ctx context.Context,
			gotViewer *models.UserSession,
			target string,
		) (*contract.EffectivePermissionsResult, error) {
			readerCalled = true
			if bound, _ := ctx.Value(permissionSnapshotContextKey{}).(bool); !bound {
				t.Fatal("permission reader did not receive transaction context")
			}
			if gotViewer != viewer || target != "target" {
				t.Fatalf("reader authority = %#v/%q", gotViewer, target)
			}
			return want, nil
		}),
		permissionSnapshotTransactionsFunc(func(
			ctx context.Context,
			run func(context.Context) error,
		) error {
			return run(context.WithValue(ctx, permissionSnapshotContextKey{}, true))
		}),
		permissionStateSourceFunc(func(ctx context.Context) (persistence.PermissionState, error) {
			if bound, _ := ctx.Value(permissionSnapshotContextKey{}).(bool); !bound {
				t.Fatal("Permission State did not join transaction context")
			}
			return persistence.PermissionState{
				Version: floor, EnforcementMode: persistence.PermissionStateEnforcedMode,
			}, nil
		}),
		effectiveObjectPermissionCheckerFunc(allowEffectiveObjectPermission),
	)
	if err != nil {
		t.Fatal(err)
	}
	got, err := projection.EffectivePermissions(
		t.Context(), viewer, "target", floor,
	)
	if err != nil || got != want || !readerCalled {
		t.Fatalf("EffectivePermissions() = %#v, %v; called=%v", got, err, readerCalled)
	}
}

func TestEffectivePermissionProjectionRejectsChangedStateBeforeReading(t *testing.T) {
	floor := permissionProjectionVersion(t, 7)
	current := permissionProjectionVersion(t, 8)
	readerCalled := false
	projection, err := newEffectivePermissionProjection(
		effectivePermissionReaderFunc(func(
			context.Context,
			*models.UserSession,
			string,
		) (*contract.EffectivePermissionsResult, error) {
			readerCalled = true
			return &contract.EffectivePermissionsResult{}, nil
		}),
		permissionSnapshotTransactionsFunc(func(
			ctx context.Context,
			run func(context.Context) error,
		) error {
			return run(ctx)
		}),
		permissionStateSourceFunc(func(context.Context) (persistence.PermissionState, error) {
			return persistence.PermissionState{
				Version: current, EnforcementMode: persistence.PermissionStateEnforcedMode,
			}, nil
		}),
		effectiveObjectPermissionCheckerFunc(allowEffectiveObjectPermission),
	)
	if err != nil {
		t.Fatal(err)
	}
	_, err = projection.EffectivePermissions(
		t.Context(),
		&models.UserSession{ID: "viewer", ProfileID: "profile"},
		"",
		floor,
	)
	var failure *permissionsemantic.Failure
	if !errors.As(err, &failure) ||
		failure.Category != permissionsemantic.FailureUnavailable ||
		readerCalled {
		t.Fatalf("changed state = %v, called=%v", err, readerCalled)
	}
}

func TestEffectivePermissionProjectionReusesOwnedTransactionAndRejectsMissingOwner(t *testing.T) {
	floor := permissionProjectionVersion(t, 11)
	transactionsStarted := 0
	readerCalled := 0
	projection, err := newEffectivePermissionProjection(
		effectivePermissionReaderFunc(func(
			ctx context.Context,
			_ *models.UserSession,
			_ string,
		) (*contract.EffectivePermissionsResult, error) {
			if bound, _ := ctx.Value(permissionSnapshotContextKey{}).(bool); !bound {
				t.Fatal("permission reader escaped the owned transaction")
			}
			readerCalled++
			return &contract.EffectivePermissionsResult{}, nil
		}),
		permissionSnapshotTransactionsFunc(func(
			ctx context.Context,
			run func(context.Context) error,
		) error {
			transactionsStarted++
			return run(context.WithValue(ctx, permissionSnapshotContextKey{}, true))
		}),
		permissionStateSourceFunc(func(ctx context.Context) (persistence.PermissionState, error) {
			if bound, _ := ctx.Value(permissionSnapshotContextKey{}).(bool); !bound {
				t.Fatal("Permission State escaped the owned transaction")
			}
			return persistence.PermissionState{
				Version: floor, EnforcementMode: persistence.PermissionStateEnforcedMode,
			}, nil
		}),
		effectiveObjectPermissionCheckerFunc(allowEffectiveObjectPermission),
	)
	if err != nil {
		t.Fatal(err)
	}
	viewer := &models.UserSession{ID: "viewer", ProfileID: "profile"}
	_, err = projection.EffectivePermissionsWithinTransaction(
		context.WithValue(t.Context(), permissionSnapshotContextKey{}, true),
		viewer,
		"viewer",
		floor,
	)
	if err != nil || transactionsStarted != 0 || readerCalled != 1 {
		t.Fatalf(
			"transaction-bound projection = %v; started=%d read=%d",
			err, transactionsStarted, readerCalled,
		)
	}
	_, err = projection.EffectivePermissionsWithinTransaction(
		t.Context(), viewer, "viewer", floor,
	)
	var failure *permissionsemantic.Failure
	if !errors.As(err, &failure) || failure.Category != permissionsemantic.FailureInternal ||
		transactionsStarted != 0 || readerCalled != 1 {
		t.Fatalf(
			"missing transaction owner = %v; started=%d read=%d",
			err, transactionsStarted, readerCalled,
		)
	}
}

func TestEffectivePermissionProjectionDelegatesObjectDecisionToPermissionOwner(t *testing.T) {
	called := 0
	viewer := &models.UserSession{ID: "viewer", ProfileID: "profile"}
	projection, err := newEffectivePermissionProjection(
		effectivePermissionReaderFunc(func(
			context.Context,
			*models.UserSession,
			string,
		) (*contract.EffectivePermissionsResult, error) {
			return &contract.EffectivePermissionsResult{}, nil
		}),
		permissionSnapshotTransactionsFunc(func(
			ctx context.Context,
			run func(context.Context) error,
		) error {
			return run(ctx)
		}),
		permissionStateSourceFunc(func(context.Context) (persistence.PermissionState, error) {
			return persistence.PermissionState{}, nil
		}),
		effectiveObjectPermissionCheckerFunc(func(
			ctx context.Context,
			objectAPIName string,
			operation string,
			user *models.UserSession,
		) bool {
			called++
			return ctx != nil && objectAPIName == "GatedObject" &&
				operation == "Read" && user == viewer
		}),
	)
	if err != nil {
		t.Fatal(err)
	}
	if !projection.CheckObjectPermissionWithUser(
		t.Context(), "GatedObject", "Read", viewer,
	) || called != 1 {
		t.Fatalf("authoritative object decision was not delegated; calls=%d", called)
	}
	if projection.CheckObjectPermissionWithUser(
		t.Context(), "GatedObject", "Read", nil,
	) || called != 1 {
		t.Fatalf("nil user reached object decision owner; calls=%d", called)
	}
	visible, err := projection.CheckFieldVisibilityWithUser(
		t.Context(), "GatedObject", "VisibleField", viewer,
	)
	if err != nil || !visible {
		t.Fatalf("authoritative field decision = %v, %v", visible, err)
	}
	visible, err = projection.CheckFieldVisibilityWithUser(
		t.Context(), "GatedObject", "VisibleField", nil,
	)
	if err != nil || visible {
		t.Fatalf("nil-user field decision = %v, %v", visible, err)
	}
}

func permissionProjectionVersion(
	t *testing.T,
	revision uint64,
) permissionstatev1.Version {
	t.Helper()
	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
}

type selectedPermissionReaderFunc func(context.Context, *models.UserSession, string, permissionsemantic.Selection) (*contract.EffectivePermissionsResult, error)

func (f selectedPermissionReaderFunc) EffectivePermissions(context.Context, *models.UserSession, string) (*contract.EffectivePermissionsResult, error) {
	return nil, errors.New("unexpected unbounded permission read")
}
func (f selectedPermissionReaderFunc) EffectivePermissionsSelected(ctx context.Context, user *models.UserSession, target string, selection permissionsemantic.Selection) (*contract.EffectivePermissionsResult, error) {
	return f(ctx, user, target, selection)
}
func TestSelectedPermissionProjectionSharesNavigationTransaction(t *testing.T) {
	floor := permissionProjectionVersion(t, 7)
	begins, reads := 0, 0
	selected := permissionsemantic.Selection{Sections: []permissionsemantic.Section{permissionsemantic.SectionNavigationPermissions}}
	reader := selectedPermissionReaderFunc(func(ctx context.Context, _ *models.UserSession, _ string, selection permissionsemantic.Selection) (*contract.EffectivePermissionsResult, error) {
		if bound, _ := ctx.Value(permissionSnapshotContextKey{}).(bool); !bound {
			t.Fatal("permission read escaped caller transaction")
		}
		if len(selection.Sections) != 1 || selection.Sections[0] != permissionsemantic.SectionNavigationPermissions {
			t.Fatal("selection broadened")
		}
		reads++
		return &contract.EffectivePermissionsResult{}, nil
	})
	projection, err := newEffectivePermissionProjection(reader, permissionSnapshotTransactionsFunc(func(ctx context.Context, run func(context.Context) error) error {
		begins++
		return run(context.WithValue(ctx, permissionSnapshotContextKey{}, true))
	}), permissionStateSourceFunc(func(context.Context) (persistence.PermissionState, error) {
		return persistence.PermissionState{Version: floor, EnforcementMode: persistence.PermissionStateEnforcedMode}, nil
	}), effectiveObjectPermissionCheckerFunc(allowEffectiveObjectPermission))
	if err != nil {
		t.Fatal(err)
	}
	user := &models.UserSession{ID: "viewer", ProfileID: "profile"}
	if _, err := projection.EffectivePermissionsSelectedInTransaction(t.Context(), user, "", selected, floor); err == nil {
		t.Fatal("missing transaction accepted")
	}
	if begins != 0 || reads != 0 {
		t.Fatal("invalid transaction performed reads")
	}
	ctx := context.WithValue(t.Context(), permissionSnapshotContextKey{}, true)
	if _, err := projection.EffectivePermissionsSelectedInTransaction(ctx, user, "", selected, floor); err != nil {
		t.Fatal(err)
	}
	if begins != 0 || reads != 1 {
		t.Fatalf("nested transaction opened: begins=%d reads=%d", begins, reads)
	}
	if _, err := projection.EffectivePermissionsSelected(t.Context(), user, "", selected, floor); err != nil {
		t.Fatal(err)
	}
	if begins != 1 || reads != 2 {
		t.Fatalf("standalone read lost transaction: begins=%d reads=%d", begins, reads)
	}
}
