package permissions

import (
	"encoding/json"
	"reflect"
	"testing"

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

func TestAssignmentPolicyDeclarationIsDeepAndDark(t *testing.T) {
	registration, err := newPermissionSetAssignmentPolicyComponentRegistration()
	if err != nil {
		t.Fatal(err)
	}
	if registration.Contract.Kind != PermissionSetAssignmentPolicyComponentKind ||
		registration.Contract.Phase != assignmentPolicyInstallPlanPhase ||
		registration.Contract.OwnerDiscriminator != cp.ComponentOwnerStrategyOwnerPack ||
		registration.Contract.CollisionKey != assignmentPolicyTargetCollisionDomain ||
		registration.Capability.Kind != PermissionSetAssignmentPolicyComponentKind ||
		!registration.Capability.Installable || registration.BuildHandler == nil {
		t.Fatalf("registration = %#v", registration)
	}
	if registration.ManagedProvider != nil {
		t.Fatal("dark declaration unexpectedly exposes a legacy managed provider")
	}
	if len(registration.Contract.Manageabilities) != 1 {
		t.Fatalf("manageabilities = %#v", registration.Contract.Manageabilities)
	}
	if _, ok := registration.Contract.Manageabilities[cp.ManageabilityLocked]; !ok {
		t.Fatalf("manageabilities = %#v", registration.Contract.Manageabilities)
	}
	gotWriteTables := make([]string, len(registration.Contract.WriteSet))
	for index, surface := range registration.Contract.WriteSet {
		gotWriteTables[index] = surface.Table
	}
	wantWriteTables := []string{
		schemacatalog.AssignmentPolicyClaimTable,
		schemacatalog.AssignmentPolicyReleaseReceiptTable,
		schemacatalog.AssignmentPolicySelectorTable,
		schemacatalog.AssignmentPolicyTargetStateTable,
		schemacatalog.AssignmentPolicyTombstoneReceiptTable,
		"PermissionSetAssignment",
	}
	if !reflect.DeepEqual(gotWriteTables, wantWriteTables) {
		t.Fatalf("write tables = %v, want %v", gotWriteTables, wantWriteTables)
	}
}

func TestAssignmentPolicyBuildUsesCanonicalProjectionAndDefinitionCollision(t *testing.T) {
	registration, err := newPermissionSetAssignmentPolicyComponentRegistration()
	if err != nil {
		t.Fatal(err)
	}
	payload := []byte(`{
		"schema":"nexuscrm.permission-set-assignment-policy/v1",
		"permissionSet":"Sales_User",
		"profiles_any_of":["System Administrator","Standard User"]
	}`)
	build := &permissionSetDefinitionBuildContext{packName: "salesforce-crm-standard"}
	request := cp.ComponentBuildRequest{
		PackName: build.packName,
		Component: cp.ManifestComponent{
			Kind:          PermissionSetAssignmentPolicyComponentKind,
			BusinessKey:   "sales-users",
			Manageability: cp.ManageabilityLocked,
		},
		Payload: payload,
	}
	if err := registration.BuildHandler(build, request); err != nil {
		t.Fatal(err)
	}
	if len(build.managed) != 1 {
		t.Fatalf("managed components = %d", len(build.managed))
	}
	managed := build.managed[0]
	if len(managed.CollisionClaims) != 1 ||
		managed.CollisionClaims[0].Namespace != assignmentPolicyTargetCollisionDomain ||
		managed.CollisionClaims[0].Key != "sales_user" {
		t.Fatalf("collision claims = %#v", managed.CollisionClaims)
	}
	var projection assignmentPolicyDeclarationProjection
	if err := json.Unmarshal(managed.ExpectedProjection, &projection); err != nil {
		t.Fatal(err)
	}
	if projection.OwnerPack != build.packName || projection.PolicyKey != "sales-users" ||
		projection.PermissionSet != "Sales_User" ||
		!reflect.DeepEqual(projection.ProfilesAnyOf, []string{"Standard User", "System Administrator"}) {
		t.Fatalf("projection = %#v", projection)
	}

	managedRequest := cp.ComponentManagedRequest{
		PackName:           request.PackName,
		Component:          request.Component,
		Payload:            payload,
		ExpectedProjection: managed.ExpectedProjection,
	}
	policy, source, expected, err := assignmentPolicyExpectedRequest(managedRequest)
	if err != nil {
		t.Fatal(err)
	}
	if source.OwnerPack() != request.PackName || source.PolicyKey() != request.Component.BusinessKey ||
		policy.PermissionSet != "Sales_User" || !reflect.DeepEqual(expected, projection) {
		t.Fatalf("managed request = %#v / %#v / %#v", policy, source, expected)
	}
	managedRequest.ExpectedProjection = append([]byte(" "), managedRequest.ExpectedProjection...)
	if _, _, _, err := assignmentPolicyExpectedRequest(managedRequest); err == nil {
		t.Fatal("non-canonical expected projection was accepted")
	}
}

func TestAssignmentPolicyBuildRejectsBoundaryMismatches(t *testing.T) {
	registration, err := newPermissionSetAssignmentPolicyComponentRegistration()
	if err != nil {
		t.Fatal(err)
	}
	validPayload := []byte(`{"schema":"nexuscrm.permission-set-assignment-policy/v1","permissionSet":"P","profiles_any_of":["Standard User"]}`)
	valid := cp.ComponentBuildRequest{
		PackName: "pack-a",
		Component: cp.ManifestComponent{
			Kind:          PermissionSetAssignmentPolicyComponentKind,
			BusinessKey:   "policy-a",
			Manageability: cp.ManageabilityLocked,
		},
		Payload: validPayload,
	}
	for _, mutate := range []func(*cp.ComponentBuildRequest){
		func(request *cp.ComponentBuildRequest) { request.PackName = "pack-b" },
		func(request *cp.ComponentBuildRequest) { request.Component.Kind = "other" },
		func(request *cp.ComponentBuildRequest) {
			request.Component.Manageability = cp.ManageabilityDefaultsOnly
		},
		func(request *cp.ComponentBuildRequest) { request.Component.BusinessKey = " policy-a" },
		func(request *cp.ComponentBuildRequest) { request.Payload = []byte(`{}`) },
	} {
		request := valid
		mutate(&request)
		if err := registration.BuildHandler(&permissionSetDefinitionBuildContext{packName: "pack-a"}, request); err == nil {
			t.Fatalf("mutated request %#v succeeded", request)
		}
	}
}
