package persistence

import (
	"reflect"
	"strings"
	"testing"

	"github.com/nexuscrm/kernel/pkg/schemacatalog"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestPermissionSetDirectTargetBuildersClassifyManagedOwnerGuards(t *testing.T) {
	assertGuarded := func(t *testing.T, target permissionStateDirectTarget) {
		t.Helper()
		if target.permissionSetDefinitionDisposition != permissionSetDefinitionDirectMutationRejectManaged {
			t.Fatalf("target %s %q disposition = %q, want managed-owner rejection", target.tableName, target.whereSQL, target.permissionSetDefinitionDisposition)
		}
		for _, prelock := range target.prelocks {
			if prelock.tableName == constants.TablePermissionSet && prelock.rejectManagedPermissionSetOwner {
				return
			}
		}
		t.Fatalf("target %s %q has no managed Permission Set owner guard", target.tableName, target.whereSQL)
	}
	assertUnguarded := func(t *testing.T, target permissionStateDirectTarget) {
		t.Helper()
		for _, prelock := range target.prelocks {
			if prelock.rejectManagedPermissionSetOwner {
				t.Fatalf("target %s %q unexpectedly rejects managed Permission Set owner", target.tableName, target.whereSQL)
			}
		}
	}
	assertDetach := func(t *testing.T, target permissionStateDirectTarget) {
		t.Helper()
		if target.permissionSetDefinitionDisposition != permissionSetDefinitionDirectMutationDetachOnly {
			t.Fatalf("target %s %q disposition = %q, want detach-only", target.tableName, target.whereSQL, target.permissionSetDefinitionDisposition)
		}
		for _, prelock := range target.prelocks {
			if prelock.tableName == constants.TablePermissionSet && !prelock.rejectManagedPermissionSetOwner {
				return
			}
		}
		t.Fatalf("target %s %q has no lock-only Permission Set prelock", target.tableName, target.whereSQL)
	}

	assertGuarded(t, managedPermissionSetStateTarget(permissionStateDirectEqual(constants.FieldID), "ps-1"))
	assertGuarded(t, objectPermissionStateTargets([]models.ObjectPermissions{{ParentID: "ps-1", SobjectType: "Account"}})[0])
	assertGuarded(t, fieldPermissionStateTargets([]models.FieldPermissions{{ParentID: "ps-1", SobjectType: "Account", Field: "Name"}})[0])
	assertGuarded(t, permissionSetRecordTypePermissionStateTargets("ps-1", "rt-1", true)[0])
	recordTypeDetach := permissionSetRecordTypePermissionStateTargets("ps-1", "rt-1", false)[0]
	assertUnguarded(t, recordTypeDetach)
	assertDetach(t, recordTypeDetach)
	assertGuarded(t, permissionSetGroupPermissionStateTargets("psg-1", []string{"ps-1"}, false)[1])
	groupDeleteTargets := permissionSetGroupPermissionStateTargets("psg-1", nil, true)
	assertUnguarded(t, groupDeleteTargets[0])
	assertDetach(t, groupDeleteTargets[1])
	assertGuarded(t, customPermissionDeletePermissionStateTargets("cp-1")[0])
	assertGuarded(t, permissionSetCustomPermissionStateTargets("ps-1", "cp-1")[0])
	dropTargets := dropTableMetadataPermissionStateTargets("Account")
	assertGuarded(t, dropTargets[0])
	assertGuarded(t, dropTargets[2])
	assertDetach(t, dropTargets[4])
	for _, targetIndex := range []int{3, 4} {
		target := dropTargets[targetIndex]
		if strings.Contains(target.whereSQL, "SobjectType") ||
			!strings.Contains(target.whereSQL, "ObjectId") {
			t.Fatalf(
				"drop target %d predicate = %q, want ObjectId-derived ownership",
				targetIndex,
				target.whereSQL,
			)
		}
	}
	deleteTargets := permissionSetDeletePermissionStateTargets("ps-1")
	assertGuarded(t, deleteTargets[0])
	cascadedDefinitionTables := map[string]struct{}{}
	for _, target := range deleteTargets[1:] {
		if target.tableName == constants.TablePermissionSetAssignment {
			continue
		}
		if target.permissionSetDefinitionDisposition != permissionSetDefinitionDirectMutationCascade {
			t.Fatalf("PermissionSet cascade target %s disposition = %q, want checked-root cascade", target.tableName, target.permissionSetDefinitionDisposition)
		}
		cascadedDefinitionTables[strings.ToLower(target.tableName)] = struct{}{}
	}
	inventory, err := schemacatalog.ManagedPermissionSetDefinitionInventory()
	if err != nil {
		t.Fatalf("ManagedPermissionSetDefinitionInventory(): %v", err)
	}
	mutationInventory, err := schemacatalog.ManagedPermissionStateMutationInventory()
	if err != nil {
		t.Fatalf("ManagedPermissionStateMutationInventory(): %v", err)
	}
	mutationTables := map[string]struct{}{}
	for _, surface := range mutationInventory.Surfaces() {
		mutationTables[strings.ToLower(surface.TableName)] = struct{}{}
	}
	for _, relation := range inventory.Relations() {
		if relation.Role != schemacatalog.PermissionSetDefinitionRoleNegativeEdge {
			continue
		}
		tableKey := strings.ToLower(relation.TableName)
		if _, projected := mutationTables[tableKey]; !projected {
			continue
		}
		if _, ok := cascadedDefinitionTables[tableKey]; !ok {
			t.Fatalf("PermissionSet delete plan omits negative edge %s", relation.TableName)
		}
	}
	assertDetach(t, deleteAppPermissionStateTargets("app-1")[1])
}

func TestPermissionSetGroupUpdatePrelockRejectsOnlyDesiredManagedParents(t *testing.T) {
	target := permissionSetGroupPermissionStateTargets("psg-1", []string{"ps-desired"}, false)[1]
	if len(target.prelocks) != 2 {
		t.Fatalf("prelocks = %#v, want combined lock plus desired-owner check", target.prelocks)
	}
	if target.prelocks[0].rejectManagedPermissionSetOwner {
		t.Fatal("combined old/final prelock unexpectedly rejects existing-only managed parents")
	}
	if !target.prelocks[1].rejectManagedPermissionSetOwner {
		t.Fatal("desired subset prelock does not reject managed parents")
	}
	if got, want := target.prelocks[1].args, []interface{}{"ps-desired"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("desired subset args = %#v, want %#v", got, want)
	}
}

func TestManagedPermissionSetOwnerUsesNullBoundary(t *testing.T) {
	for _, tc := range []struct {
		name string
		row  []interface{}
		want bool
	}{
		{name: "null is administrator owned", row: []interface{}{"ps-1", nil}, want: false},
		{name: "blank is still pack owned", row: []interface{}{"ps-1", ""}, want: true},
		{name: "whitespace is still pack owned", row: []interface{}{"ps-1", " "}, want: true},
	} {
		t.Run(tc.name, func(t *testing.T) {
			if got := permissionStateDirectRowsContainManagedPermissionSet([][]interface{}{tc.row}); got != tc.want {
				t.Fatalf("managed owner = %t, want %t", got, tc.want)
			}
		})
	}
}
