package permissions

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"github.com/nexuscrm/kernel/pkg/schemacatalog"
	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

func TestAssignmentPolicyLifecycleRegistrationDerivesDefinitionNegativeEdges(t *testing.T) {
	_, lifecycle, err := newAssignmentPolicyTestManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	inventory, err := schemacatalog.ManagedPermissionSetDefinitionInventory()
	if err != nil {
		t.Fatal(err)
	}
	got := make(map[string]struct{}, len(lifecycle.ReadSet()))
	for _, surface := range lifecycle.ReadSet() {
		got[surface.Table] = struct{}{}
	}
	for _, relation := range inventory.Relations() {
		if relation.Role != schemacatalog.PermissionSetDefinitionRoleNegativeEdge ||
			relation.TableName == schemacatalog.AssignmentPolicyClaimTable {
			continue
		}
		if _, ok := got[relation.TableName]; !ok {
			t.Fatalf("negative-edge table %s is absent from lifecycle read set", relation.TableName)
		}
	}
	for _, table := range []string{
		schemacatalog.AssignmentPolicyClaimTable,
		schemacatalog.AssignmentPolicyReleaseReceiptTable,
		schemacatalog.AssignmentPolicySelectorTable,
		schemacatalog.AssignmentPolicyTargetStateTable,
		schemacatalog.AssignmentPolicyTombstoneReceiptTable,
		"PermissionSet",
		"PermissionSetAssignment",
		"Profile",
		"User",
	} {
		if _, ok := got[table]; !ok {
			t.Fatalf("required read table %s is absent", table)
		}
	}
}

func TestAssignmentPolicyLifecycleRegistrationBudgetsEveryExactSetLockTarget(t *testing.T) {
	_, lifecycle, err := newAssignmentPolicyTestManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	// An exact-set replan locks the global assignment-policy serialization
	// target, the pack set, every desired logical source, every current logical
	// source, and every current physical target.
	want := 2 + 3*assignmentPolicyMaximumRows
	if got := lifecycle.Budget().MaxTargets; got != want {
		t.Fatalf("maximum targets = %d, want exact-set lock ceiling %d", got, want)
	}
}

func TestAssignmentPolicyLifecycleSetEstimateAllowsManySmallReleases(t *testing.T) {
	_, lifecycle, err := newAssignmentPolicyTestManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	store := assignmentPolicyLifecycleStore()
	delete(store.rows, "PermissionSet")
	for index := 0; index < 34; index++ {
		id := fmt.Sprintf("ps-%02d", index)
		name := fmt.Sprintf("Removal_%02d", index)
		key := fmt.Sprintf("policy_%02d", index)
		store.rows["PermissionSet"] = append(store.rows["PermissionSet"], cp.ComponentRow{
			"Id": id, "Name": name, "OwnerPack": "definitions",
			"IsActive": true, "IsDeleted": false, "IsCustom": true,
			"ProfileId": nil, "IsOwnedByProfile": false,
		})
		store.rows[schemacatalog.AssignmentPolicyClaimTable] = append(
			store.rows[schemacatalog.AssignmentPolicyClaimTable],
			cp.ComponentRow{
				"Id": fmt.Sprintf("claim-%02d", index), "PermissionSetId": id,
				"OwnerPack": "app", "PolicyKey": key, "PayloadHash": []byte("old"),
			},
		)
		store.rows[schemacatalog.AssignmentPolicyTargetStateTable] = append(
			store.rows[schemacatalog.AssignmentPolicyTargetStateTable],
			cp.ComponentRow{
				"Id":           fmt.Sprintf("state-%02d", index),
				"TargetDomain": permissionSetDefinitionManagedTargetDomain,
				"PhysicalKey":  []byte(strings.ToLower(name)), "PermissionSetId": id,
				"Suspended": false, "WorkGeneration": uint64(1), "AppliedGeneration": uint64(1),
				"ResolutionState": assignmentPolicyResolutionResolved,
			},
		)
	}
	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	plan, err := lifecycle.Lifecycle().Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if err := lifecycle.ValidatePlanCapabilities(plan); err != nil {
		t.Fatalf("34 small exact-set releases exceeded registration budget: %v", err)
	}
}
