package permissions

import (
	"context"
	"reflect"
	"testing"

	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionauthoringv1"
)

func TestPermissionAuthoringSnapshotReadsOneLockedFiveFamilyImage(t *testing.T) {
	fixture := newPermissionAuthoringFixture(t)
	store := &fakePermissionAuthoringSnapshotStore{
		fakePermissionAuthoringTargets: fixture.targets,
		systemCatalog: []models.SystemPermission{
			{APIName: "ViewSetup", Label: "View Setup", Category: "Setup", IsActive: true},
			{APIName: "Inactive", Label: "Inactive", Category: "Setup", IsActive: false},
		},
		systemGrants: map[string]bool{"ViewSetup": true},
		objectRows: []ports.PermissionAuthoringObjectGrant{
			{ResourceType: "Account", Read: true},
			{ResourceType: "account", Edit: true},
		},
		fieldRows: []ports.PermissionAuthoringFieldGrant{
			{ResourceType: "Account", FieldName: "Account.Name", Read: true},
			{ResourceType: "Account", FieldName: "Name", Edit: true},
		},
		recordTypeCatalog: []ports.RecordTypeCatalogEntry{
			{RecordTypeID: "rt-1", ObjectAPIName: "Account", Name: "Business", Label: "Business", IsActive: true},
			{RecordTypeID: "rt-2", ObjectAPIName: "Account", Name: "Consumer", Label: "Consumer", IsActive: true},
			{RecordTypeID: "rt-absent", ObjectAPIName: "Account", Name: "Partner", Label: "Partner", IsActive: true},
		},
		recordTypeRows: []models.PermissionSetRecordTypeVisibility{
			{RecordTypeID: "rt-1", IsVisible: true},
			{RecordTypeID: "rt-2", IsVisible: false},
		},
		customCatalog: []models.SystemCustomPermission{
			{ID: "cp-1", APIName: "Export_Data", Label: "Export Data", IsActive: true},
		},
		customRows: []models.PermissionSetCustomPermission{{CustomPermissionID: "cp-1"}},
	}
	service, err := NewPermissionAuthoringSnapshotService(
		fixture.transactions,
		store,
		fixture.policy,
		fixture.receipts,
	)
	if err != nil {
		t.Fatal(err)
	}
	floor := uint64(7)
	response, err := service.Read(fixture.ctx, fixture.invocation, permissionauthoringv1.SnapshotRequest{
		Target: permissionauthoringv1.Target{
			Kind: permissionauthoringv1.TargetProfile,
			ID:   "profile-sales",
		},
		MinCatalogRevision: &floor,
	})
	if err != nil {
		t.Fatalf("Read(): %v", err)
	}
	if fixture.transactions.calls != 1 || store.transaction == 0 || store.transaction != fixture.transactions.calls {
		t.Fatalf("snapshot transaction calls=%d observed=%d", fixture.transactions.calls, store.transaction)
	}
	if response.CanonicalPermissionSetID != "permission-set-1" ||
		response.AuthoringToken != permissionAuthoringTokenForTest(t, "permission-set-1", 7) {
		t.Fatalf("snapshot root = %+v", response)
	}
	if len(response.Grants.System) != 1 || !response.Grants.System[0].Enabled ||
		len(response.Grants.Objects) != 1 || !response.Grants.Objects[0].Edit ||
		len(response.Grants.Fields) != 1 || response.Grants.Fields[0].FieldName != "Name" ||
		!response.Grants.Fields[0].Read || !response.Grants.Fields[0].Edit ||
		len(response.Grants.RecordTypes) != 2 || !response.Grants.RecordTypes[0].Visible || response.Grants.RecordTypes[1].Visible ||
		response.Grants.RecordTypes[0].RecordTypeID != "rt-1" || response.Grants.RecordTypes[1].RecordTypeID != "rt-2" ||
		len(response.Grants.Custom) != 1 || !response.Grants.Custom[0].Enabled {
		t.Fatalf("snapshot did not assemble one complete logical image: %+v", response.Grants)
	}
}

func TestPermissionAuthoringStatusReturnsFrozenTerminalWithoutTargetResolution(t *testing.T) {
	fixture := newPermissionAuthoringFixture(t)
	request := permissionAuthoringRequestForTest(t)
	terminal, err := fixture.service.Save(fixture.ctx, fixture.invocation, request)
	if err != nil {
		t.Fatalf("Save(): %v", err)
	}
	digest, err := permissionauthoringv1.CommandDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	// Terminal outcomes stay frozen and replayable across the semantic cutover;
	// only nonterminal commands are forbidden from crossing planners.
	scope := permissionAuthoringScopeDigest(fixture.invocation.AuthorityScope, fixture.actor.ID)
	key := commandKey(scope, request.CommandID)
	row := fixture.commands.byKey[key]
	row.SemanticMappingRevision--
	fixture.commands.byKey[key] = row
	beforeTransactions := fixture.transactions.calls
	beforeResolve := fixture.targets.resolveCalls
	fixture.targets.found = false
	fixture.targets.resolveErr = context.Canceled
	fixture.policy.authorizeErr = context.Canceled
	response, err := fixture.service.Status(fixture.ctx, fixture.invocation, permissionauthoringv1.StatusRequest{
		CommandID:     request.CommandID,
		CommandDigest: digest,
	})
	if err != nil {
		t.Fatalf("Status(): %v", err)
	}
	if response.State != permissionauthoringv1.CommandCompleted || response.Result == nil ||
		!reflect.DeepEqual(*response.Result, terminal) {
		t.Fatalf("Status() = %+v, want frozen %+v", response, terminal)
	}
	if fixture.transactions.calls != beforeTransactions || fixture.targets.resolveCalls != beforeResolve {
		t.Fatalf("Status repeated mutable work: tx %d->%d resolve %d->%d",
			beforeTransactions, fixture.transactions.calls, beforeResolve, fixture.targets.resolveCalls)
	}
}

type fakePermissionAuthoringSnapshotStore struct {
	*fakePermissionAuthoringTargets
	transaction       int
	systemCatalog     []models.SystemPermission
	systemGrants      map[string]bool
	objectRows        []ports.PermissionAuthoringObjectGrant
	fieldRows         []ports.PermissionAuthoringFieldGrant
	recordTypeCatalog []ports.RecordTypeCatalogEntry
	recordTypeRows    []models.PermissionSetRecordTypeVisibility
	customCatalog     []models.SystemCustomPermission
	customRows        []models.PermissionSetCustomPermission
}

func (store *fakePermissionAuthoringSnapshotStore) observe(ctx context.Context) {
	current := permissionAuthoringTx(ctx)
	if store.transaction == 0 {
		store.transaction = current
	}
	if current == 0 || current != store.transaction {
		panic("permission authoring snapshot escaped one transaction")
	}
}

func (store *fakePermissionAuthoringSnapshotStore) ResolvePermissionAuthoringTarget(
	ctx context.Context,
	kind string,
	id string,
) (string, bool, error) {
	store.observe(ctx)
	return store.fakePermissionAuthoringTargets.ResolvePermissionAuthoringTarget(ctx, kind, id)
}

func (store *fakePermissionAuthoringSnapshotStore) LockPermissionSetAuthoring(
	ctx context.Context,
	id string,
) (ports.LockedPermissionAuthoringAggregate, bool, error) {
	store.observe(ctx)
	return store.fakePermissionAuthoringTargets.LockPermissionSetAuthoring(ctx, id)
}

func (store *fakePermissionAuthoringSnapshotStore) ListSystemPermissions(ctx context.Context) ([]models.SystemPermission, error) {
	store.observe(ctx)
	return append([]models.SystemPermission(nil), store.systemCatalog...), nil
}

func (store *fakePermissionAuthoringSnapshotStore) GetPermissionSetGrants(ctx context.Context, _ string) (map[string]bool, error) {
	store.observe(ctx)
	return store.systemGrants, nil
}

func (store *fakePermissionAuthoringSnapshotStore) ListPermissionAuthoringObjectGrants(ctx context.Context, _ string) ([]ports.PermissionAuthoringObjectGrant, error) {
	store.observe(ctx)
	return append([]ports.PermissionAuthoringObjectGrant(nil), store.objectRows...), nil
}

func (store *fakePermissionAuthoringSnapshotStore) LockPermissionAuthoringFieldGrants(ctx context.Context, _ string) ([]ports.PermissionAuthoringFieldGrant, error) {
	store.observe(ctx)
	return append([]ports.PermissionAuthoringFieldGrant(nil), store.fieldRows...), nil
}

func (store *fakePermissionAuthoringSnapshotStore) ListRecordTypeCatalog(ctx context.Context) ([]ports.RecordTypeCatalogEntry, error) {
	store.observe(ctx)
	return append([]ports.RecordTypeCatalogEntry(nil), store.recordTypeCatalog...), nil
}

func (store *fakePermissionAuthoringSnapshotStore) GetPermissionSetRecordTypeGrants(ctx context.Context, _ string) ([]models.PermissionSetRecordTypeVisibility, error) {
	store.observe(ctx)
	return append([]models.PermissionSetRecordTypeVisibility(nil), store.recordTypeRows...), nil
}

func (store *fakePermissionAuthoringSnapshotStore) ListCustomPermissions(ctx context.Context) ([]models.SystemCustomPermission, error) {
	store.observe(ctx)
	return append([]models.SystemCustomPermission(nil), store.customCatalog...), nil
}

func (store *fakePermissionAuthoringSnapshotStore) GetPermissionSetCustomPermGrants(ctx context.Context, _ string) ([]models.PermissionSetCustomPermission, error) {
	store.observe(ctx)
	return append([]models.PermissionSetCustomPermission(nil), store.customRows...), nil
}
