package permissionadminv1

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"testing"
	"time"

	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
)

func permissionAdminTestContext(t *testing.T) context.Context {
	t.Helper()
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
	t.Cleanup(cancel)
	return ctx
}

func TestReadResponseRejectsRawCatalogOverBudgetBeforeSerialization(t *testing.T) {
	values := make([]SystemPermission, 257)
	for index := range values {
		values[index] = SystemPermission{
			PermissionID: fmt.Sprintf("Permission%05d", index),
			Label:        "Label",
			Category:     "Category",
			Description:  strings.Repeat("x", MaxDescriptionBytes),
		}
	}
	err := ValidateReadResponse(ReadRequest{Selector: ReadSystemCatalog}, ReadResponse{
		SystemCatalog: values,
		Receipts:      permissionAdminTestReceipts(),
	})
	var failure *common.Failure
	if !errors.As(err, &failure) || failure.Category != common.FailureInternal {
		t.Fatalf("oversized raw catalog = %#v, want bounded internal response failure", err)
	}
}

func permissionAdminTestReceipts() Receipts {
	return Receipts{ProtocolVersion: "1.0", CapabilityVersion: CapabilityName, CatalogRevision: 1, SchemaGeneration: 1, EngineGeneration: 1, SecurityEpoch: 1}
}

func TestReadShapeBindsCatalogAndGrantSelectors(t *testing.T) {
	ctx := permissionAdminTestContext(t)
	for _, test := range []struct {
		name string
		req  ReadRequest
		ok   bool
	}{
		{name: "catalog", req: ReadRequest{Selector: ReadSystemCatalog}, ok: true},
		{name: "grant target", req: ReadRequest{Selector: ReadSystemGrants, PermissionSetID: "ps-1"}, ok: true},
		{name: "catalog rejects target", req: ReadRequest{Selector: ReadSystemCatalog, PermissionSetID: "ps-1"}},
		{name: "grants require target", req: ReadRequest{Selector: ReadSystemGrants}},
	} {
		t.Run(test.name, func(t *testing.T) {
			err := ValidateReadRequest(ctx, test.req)
			if (err == nil) != test.ok {
				t.Fatalf("ValidateReadRequest() error = %v, want ok %t", err, test.ok)
			}
		})
	}
}

func TestReadResponseRequiresOneSortedFamily(t *testing.T) {
	request := ReadRequest{Selector: ReadCustomCatalog}
	valid := ReadResponse{CustomCatalog: []CustomPermission{
		{PermissionID: "cp-1", StableName: "CanReview", Label: "Can Review"},
		{PermissionID: "cp-2", StableName: "CanShip", Label: "Can Ship"},
	}, Receipts: permissionAdminTestReceipts()}
	if err := ValidateReadResponse(request, valid); err != nil {
		t.Fatalf("ValidateReadResponse() error = %v", err)
	}
	invalid := valid
	invalid.SystemGrants = []Grant{{PermissionID: "ApiEnabled", Enabled: true}}
	if ValidateReadResponse(request, invalid) == nil {
		t.Fatal("response with an unselected family was accepted")
	}
	unsorted := valid
	unsorted.CustomCatalog = append([]CustomPermission(nil), valid.CustomCatalog...)
	unsorted.CustomCatalog[0], unsorted.CustomCatalog[1] = unsorted.CustomCatalog[1], unsorted.CustomCatalog[0]
	if ValidateReadResponse(request, unsorted) == nil {
		t.Fatal("unsorted response was accepted")
	}
}

func TestMutationRejectsDuplicateGrantIdentity(t *testing.T) {
	ctx := permissionAdminTestContext(t)
	request := MutateRequest{Selector: MutateSystemGrants, PermissionSetID: "ps-1", Grants: []Grant{
		{PermissionID: "ApiEnabled", Enabled: true},
		{PermissionID: "ApiEnabled", Enabled: false},
	}}
	if ValidateMutateRequest(ctx, request) == nil {
		t.Fatal("duplicate grant identity was accepted")
	}
	request.Grants = request.Grants[:1]
	if err := ValidateMutateRequest(ctx, request); err != nil {
		t.Fatalf("ValidateMutateRequest() error = %v", err)
	}
	if err := ValidateMutateResponse(request, MutateResponse{Receipts: permissionAdminTestReceipts()}); err != nil {
		t.Fatalf("ValidateMutateResponse() error = %v", err)
	}
}
