package permissions

import (
	"context"
	"testing"

	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/models"
)

// TestRequiredReadPermissionSplitTolerantOfWhitespace verifies that
// splitRequiredReadPermission handles whitespace-padded and trailing-comma
// values correctly, so a RequiredReadPermission like "ViewAllData, ModifyAllData"
// (space after comma) does not silently drop the second token.
func TestRequiredReadPermissionSplitTolerantOfWhitespace(t *testing.T) {
	cases := []struct {
		name     string
		input    string
		wantLen  int
		wantToks []string
	}{
		{
			name:     "no_spaces",
			input:    "ViewAllData,ModifyAllData",
			wantLen:  2,
			wantToks: []string{"ViewAllData", "ModifyAllData"},
		},
		{
			name:     "space_after_comma",
			input:    "ViewAllData, ModifyAllData",
			wantLen:  2,
			wantToks: []string{"ViewAllData", "ModifyAllData"},
		},
		{
			name:     "trailing_comma",
			input:    "ViewAllData,",
			wantLen:  1,
			wantToks: []string{"ViewAllData"},
		},
		{
			name:     "leading_and_internal_spaces",
			input:    "  ViewAllData  ,  ModifyAllData  ",
			wantLen:  2,
			wantToks: []string{"ViewAllData", "ModifyAllData"},
		},
		// Malformed inputs: non-empty string that resolves to no valid tokens.
		// The call site in service.go must fail-closed when len==0 (#564 Codex BLOCK).
		{
			name:     "comma_only_yields_empty_split",
			input:    ",",
			wantLen:  0,
			wantToks: []string{},
		},
		{
			name:     "all_blank_yields_empty_split",
			input:    "  ,  ,  ",
			wantLen:  0,
			wantToks: []string{},
		},
		{
			name:     "many_commas_yields_empty_split",
			input:    ",,,,",
			wantLen:  0,
			wantToks: []string{},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := splitRequiredReadPermission(tc.input)
			if len(got) != tc.wantLen {
				t.Fatalf("splitRequiredReadPermission(%q): want %d tokens, got %d: %v",
					tc.input, tc.wantLen, len(got), got)
			}
			for i, want := range tc.wantToks {
				if got[i] != want {
					t.Errorf("token[%d]: want %q, got %q", i, want, got[i])
				}
			}
		})
	}
}

// TestSetupAuditTrailReadGate_ThreeTokenSplit verifies that splitRequiredReadPermission
// correctly parses the three-token RRP for _System_SetupAuditTrail, locking the
// OR-set shape so a typo or truncation in system_tables.json is caught at test time.
func TestSetupAuditTrailReadGate_ThreeTokenSplit(t *testing.T) {
	const rrp = "ViewSetupAuditTrail,ViewAllData,ModifyAllData"
	got := splitRequiredReadPermission(rrp)
	if len(got) != 3 {
		t.Fatalf("splitRequiredReadPermission(%q): want 3 tokens, got %d: %v", rrp, len(got), got)
	}
	want := []string{"ViewSetupAuditTrail", "ViewAllData", "ModifyAllData"}
	for i, w := range want {
		if got[i] != w {
			t.Errorf("token[%d]: want %q, got %q", i, w, got[i])
		}
	}
}

// grantDenyTestRepo is a minimal ports.PermissionRepository that returns
// a fixed effective-permission map for GetEffectiveSystemPermissions.
// All other methods are no-ops reusing saveTestRepo.
type grantDenyTestRepo struct {
	saveTestRepo
	effectivePerms map[string]bool
}

func (r *grantDenyTestRepo) GetEffectiveSystemPermissions(_ context.Context, _, _ string) (map[string]bool, error) {
	return r.effectivePerms, nil
}

func (r *grantDenyTestRepo) GetAllRoles(_ context.Context) ([]*models.UserRole, error) {
	return nil, nil
}

// Ensure grantDenyTestRepo satisfies the interface at compile time.
var _ ports.PermissionRepository = (*grantDenyTestRepo)(nil)

// TestSetupAuditTrailReadGate_GrantDenyMatrix verifies the OR-semantic gate:
// each of the three tokens individually grants access, and ViewSetup-only
// (the pre-#604 value) is denied.
func TestSetupAuditTrailReadGate_GrantDenyMatrix(t *testing.T) {
	ctx := context.Background()
	normalUser := &models.UserSession{
		ID:            "user1",
		ProfileID:     "profile1",
		IsSystemAdmin: false,
	}

	cases := []struct {
		name      string
		grants    map[string]bool
		wantGrant bool
	}{
		{"ViewSetupAuditTrail_grants", map[string]bool{"ViewSetupAuditTrail": true}, true},
		{"ViewAllData_grants", map[string]bool{"ViewAllData": true}, true},
		{"ModifyAllData_grants", map[string]bool{"ModifyAllData": true}, true},
		{"ViewSetup_only_denies", map[string]bool{"ViewSetup": true}, false},
		{"no_permissions_denies", map[string]bool{}, false},
	}

	rrpTokens := splitRequiredReadPermission("ViewSetupAuditTrail,ViewAllData,ModifyAllData")

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			repo := &grantDenyTestRepo{effectivePerms: tc.grants}
			ps := &PermissionService{
				repo:               repo,
				roleHierarchyCache: make(map[string]*string),
				sharingRuleCache:   make(map[string]*cachedSharingRules),
			}
			got := ps.hasAnySystemPermission(ctx, normalUser, rrpTokens)
			if got != tc.wantGrant {
				t.Errorf("hasAnySystemPermission with grants %v: want %v, got %v", tc.grants, tc.wantGrant, got)
			}
		})
	}
}
