package permissions

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"sync/atomic"
	"testing"
	"time"

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

func TestListEffectiveObjectPermissionsForSuperUserSynthesizesCustomObjectAccess(t *testing.T) {
	ctx := context.Background()
	repo := &pcRepo{systemPerms: superUserCapabilityGrant()}
	metadata := &pcMetadataStub{
		schemas: map[string]*models.ObjectMetadata{
			"MyObject__c": {
				APIName: "MyObject__c",
				Fields: []models.FieldMetadata{
					{APIName: "Name"},
				},
			},
		},
	}

	svc := makePCService(repo, metadata)
	perms, err := svc.ListEffectiveObjectPermissionsForUser(ctx, "user-1", "jira_admin")
	if err != nil {
		t.Fatalf("ListEffectiveObjectPermissionsForUser: %v", err)
	}

	perm, ok := objectPermByType(perms, "MyObject__c")
	if !ok {
		t.Fatalf("missing synthesized permission for MyObject__c: %+v", perms)
	}
	if !perm.PermissionsRead || !perm.PermissionsCreate || !perm.PermissionsEdit || !perm.PermissionsDelete {
		t.Fatalf("synthesized permission = %+v; want full access", perm)
	}
}

func TestListEffectiveFieldPermissionsForSuperUserSynthesizesFieldAccess(t *testing.T) {
	ctx := context.Background()
	repo := &pcRepo{systemPerms: superUserCapabilityGrant()}
	metadata := &pcMetadataStub{
		schemas: map[string]*models.ObjectMetadata{
			"MyObject__c": {
				APIName: "MyObject__c",
				Fields: []models.FieldMetadata{
					{APIName: "Name"},
					{APIName: "Secret__c"},
				},
			},
		},
	}

	svc := makePCService(repo, metadata)
	perms, err := svc.ListEffectiveFieldPermissionsForUser(ctx, "user-1", "jira_admin")
	if err != nil {
		t.Fatalf("ListEffectiveFieldPermissionsForUser: %v", err)
	}

	seen := map[string]models.FieldPermissions{}
	for _, perm := range perms {
		seen[strings.ToLower(perm.SobjectType)+"."+strings.ToLower(perm.Field)] = perm
	}

	for _, field := range []string{"Name", "Secret__c"} {
		perm, ok := seen["myobject__c."+strings.ToLower(field)]
		if !ok {
			t.Fatalf("missing synthesized field permission for %s: %+v", field, perms)
		}
		if !perm.PermissionsRead || !perm.PermissionsEdit {
			t.Fatalf("synthesized field permission = %+v; want read/edit true", perm)
		}
	}
}

func TestListEffectiveFieldPermissionsForSuperUserUsesProvenBulkSemantics(t *testing.T) {
	ctx := context.Background()
	repo := &pcRepo{systemPerms: superUserCapabilityGrant()}
	metadata := &projectionCountingMetadata{pcMetadataStub: &pcMetadataStub{
		schemas: map[string]*models.ObjectMetadata{
			"ZObject__c": {
				APIName: "ZObject__c",
				Fields: []models.FieldMetadata{
					{APIName: "Secret__c", ReadProtectionPolicy: constants.FieldReadProtectionBlocked},
					{APIName: constants.FieldID},
					{APIName: "Name"},
				},
			},
			"AObject__c": {
				APIName: "AObject__c",
				Fields:  []models.FieldMetadata{{APIName: "Name"}},
			},
		},
	}}

	svc := makePCService(repo, metadata)
	perms, err := svc.ListEffectiveFieldPermissionsForUser(ctx, "user-1", "jira_admin")
	if err != nil {
		t.Fatalf("ListEffectiveFieldPermissionsForUser: %v", err)
	}
	if metadata.getSchemaCalls != 0 {
		t.Fatalf("bulk superuser projection performed %d per-field schema lookups; want 0", metadata.getSchemaCalls)
	}
	if metadata.getSchemasCalls != 1 {
		t.Fatalf("bulk superuser projection loaded %d schema snapshots; want exactly 1", metadata.getSchemasCalls)
	}

	want := []models.FieldPermissions{
		{SobjectType: "AObject__c", Field: "Name", PermissionsRead: true, PermissionsEdit: true},
		{SobjectType: "ZObject__c", Field: constants.FieldID, PermissionsRead: true, PermissionsEdit: false},
		{SobjectType: "ZObject__c", Field: "Name", PermissionsRead: true, PermissionsEdit: true},
		{SobjectType: "ZObject__c", Field: "Secret__c", PermissionsRead: false, PermissionsEdit: true},
	}
	if len(perms) != len(want) {
		t.Fatalf("projected permissions = %+v; want %d rows", perms, len(want))
	}
	for i := range want {
		if perms[i] != want[i] {
			t.Fatalf("projected permission[%d] = %+v; want %+v", i, perms[i], want[i])
		}
	}
}

func TestListEffectiveFieldPermissionsForSuperUserLargeSchemaIsSinglePass(t *testing.T) {
	const fieldCount = 8789
	fields := make([]models.FieldMetadata, fieldCount)
	for i := range fields {
		fields[i] = models.FieldMetadata{APIName: fmt.Sprintf("Field%05d__c", i)}
	}
	repo := &projectionCountingRepo{pcRepo: pcRepo{systemPerms: superUserCapabilityGrant()}}
	metadata := &projectionCountingMetadata{pcMetadataStub: &pcMetadataStub{
		schemas: map[string]*models.ObjectMetadata{
			"LargeObject__c": {APIName: "LargeObject__c", Fields: fields},
		},
	}}

	perms, err := makePCService(repo, metadata).ListEffectiveFieldPermissionsForUser(
		context.Background(), "user-1", "admin",
	)
	if err != nil {
		t.Fatalf("ListEffectiveFieldPermissionsForUser: %v", err)
	}
	if len(perms) != fieldCount {
		t.Fatalf("large projection returned %d rows; want %d", len(perms), fieldCount)
	}
	if repo.systemPermissionLoads != 1 {
		t.Fatalf("large projection proved superuser authority %d times; want 1", repo.systemPermissionLoads)
	}
	if repo.fieldPermissionLoads != 0 {
		t.Fatalf("large superuser projection loaded repository FLS %d times; want 0", repo.fieldPermissionLoads)
	}
	if metadata.getSchemasCalls != 1 || metadata.getSchemaCalls != 0 {
		t.Fatalf(
			"large projection metadata calls = GetSchemas %d, GetSchema %d; want 1, 0",
			metadata.getSchemasCalls, metadata.getSchemaCalls,
		)
	}
	if perms[0].Field != "Field00000__c" || perms[len(perms)-1].Field != "Field08788__c" {
		t.Fatalf("large projection lost stable sorting: first %q, last %q", perms[0].Field, perms[len(perms)-1].Field)
	}
}

func TestListEffectiveFieldPermissionsForSuperUserCancelsDuringLargeProjection(t *testing.T) {
	fields := make([]models.FieldMetadata, 10_000)
	for i := range fields {
		fields[i] = models.FieldMetadata{APIName: fmt.Sprintf("Field%05d__c", i)}
	}
	repo := &projectionCountingRepo{pcRepo: pcRepo{systemPerms: superUserCapabilityGrant()}}
	metadata := &projectionCountingMetadata{pcMetadataStub: &pcMetadataStub{
		schemas: map[string]*models.ObjectMetadata{
			"LargeObject__c": {APIName: "LargeObject__c", Fields: fields},
		},
	}}
	ctx := newCancelAfterErrChecksContext(128)

	perms, err := makePCService(repo, metadata).ListEffectiveFieldPermissionsForUser(
		ctx, "user-1", "admin",
	)
	if perms != nil || !errors.Is(err, context.Canceled) {
		t.Fatalf("mid-projection cancellation = permissions %v, error %v; want nil, context canceled", perms, err)
	}
	if repo.systemPermissionLoads != 1 || repo.fieldPermissionLoads != 0 {
		t.Fatalf(
			"canceled projection repository loads = system %d, field %d; want 1, 0",
			repo.systemPermissionLoads, repo.fieldPermissionLoads,
		)
	}
}

func TestSuperUserBulkFieldProjectionMatchesPointDecisionHardGuards(t *testing.T) {
	fields := []models.FieldMetadata{
		{APIName: "Name"},
		{APIName: "Secret__c", ReadProtectionPolicy: constants.FieldReadProtectionBlocked},
		{APIName: constants.FieldID},
		{APIName: constants.FieldCreatedDate},
		{APIName: constants.FieldCreatedByID},
		{APIName: constants.FieldLastModifiedDate},
		{APIName: constants.FieldLastModifiedByID},
		{APIName: constants.FieldIsDeleted},
	}
	metadata := &pcMetadataStub{schemas: map[string]*models.ObjectMetadata{
		"Account": {APIName: "Account", Fields: fields},
	}}
	svc := makePCService(&pcRepo{systemPerms: superUserCapabilityGrant()}, metadata)
	ctx := WithEffectivePermissions(
		context.Background(), "user-1", "admin", superUserCapabilityGrant(), nil,
	)
	projected, err := svc.ListEffectiveFieldPermissionsForUser(ctx, "user-1", "admin")
	if err != nil {
		t.Fatalf("ListEffectiveFieldPermissionsForUser: %v", err)
	}
	byField := make(map[string]models.FieldPermissions, len(projected))
	for _, perm := range projected {
		byField[perm.Field] = perm
	}
	user := &models.UserSession{ID: "user-1", ProfileID: "admin"}
	for _, field := range fields {
		readable, err := svc.CheckFieldVisibilityWithUser(ctx, "Account", field.APIName, user)
		if err != nil {
			t.Fatalf("CheckFieldVisibilityWithUser(%s): %v", field.APIName, err)
		}
		editable, err := svc.CheckFieldEditabilityWithUser(ctx, "Account", field.APIName, user)
		if err != nil {
			t.Fatalf("CheckFieldEditabilityWithUser(%s): %v", field.APIName, err)
		}
		got, ok := byField[field.APIName]
		if !ok {
			t.Fatalf("bulk projection omitted %s", field.APIName)
		}
		if got.PermissionsRead != readable || got.PermissionsEdit != editable {
			t.Fatalf(
				"bulk projection %s = read %v edit %v; point decisions = read %v edit %v",
				field.APIName, got.PermissionsRead, got.PermissionsEdit, readable, editable,
			)
		}
	}
}

func TestFieldDecisionPreCanceledSuperUserFailsClosed(t *testing.T) {
	repo := &pcRepo{systemPerms: superUserCapabilityGrant()}
	metadata := &pcMetadataStub{schemas: map[string]*models.ObjectMetadata{
		"Account": {APIName: "Account", Fields: []models.FieldMetadata{{APIName: "Name", Createable: true, Updateable: true}}},
	}}
	svc := makePCService(repo, metadata)
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	user := &models.UserSession{ID: "user-1", ProfileID: "jira_admin"}
	if allowed, err := svc.CheckFieldVisibilityWithUser(ctx, "Account", "Name", user); allowed || !errors.Is(err, context.Canceled) {
		t.Fatalf("pre-canceled superuser read = allowed %v, error %v; want false, context canceled", allowed, err)
	}
	if allowed, err := svc.CheckFieldEditabilityWithUser(ctx, "Account", "Name", user); allowed || !errors.Is(err, context.Canceled) {
		t.Fatalf("pre-canceled superuser edit = allowed %v, error %v; want false, context canceled", allowed, err)
	}
	field := &models.FieldMetadata{APIName: "Name", Createable: true, Updateable: true}
	resolver := NewFieldAccessResolver(svc)
	if allowed, err := resolver.EffectiveCreateable(ctx, field, "Account", user); allowed || !errors.Is(err, context.Canceled) {
		t.Fatalf("pre-canceled superuser create = allowed %v, error %v; want false, context canceled", allowed, err)
	}
	if allowed, err := resolver.EffectiveUpdateable(ctx, field, "Account", user); allowed || !errors.Is(err, context.Canceled) {
		t.Fatalf("pre-canceled superuser update = allowed %v, error %v; want false, context canceled", allowed, err)
	}
	if schema, err := svc.GetEffectiveSchema(ctx, metadata.schemas["Account"], user); schema != nil || !errors.Is(err, context.Canceled) {
		t.Fatalf("pre-canceled superuser effective schema = schema %v, error %v; want nil, context canceled", schema, err)
	}
	if perms, err := svc.ListEffectiveFieldPermissionsForUser(ctx, "user-1", "jira_admin"); perms != nil || !errors.Is(err, context.Canceled) {
		t.Fatalf("pre-canceled superuser bulk projection = permissions %v, error %v; want nil, context canceled", perms, err)
	}
}

func TestListEffectiveFieldPermissionsForSuperUserObjectStaysObjectBounded(t *testing.T) {
	ctx := context.Background()
	repo := &pcRepo{systemPerms: superUserCapabilityGrant()}
	metadata := &pcMetadataStub{
		schemas: map[string]*models.ObjectMetadata{
			"Account": {
				APIName: "Account",
				Fields:  []models.FieldMetadata{{APIName: "Name"}, {APIName: "Secret__c"}},
			},
			"Contact": {
				APIName: "Contact",
				Fields:  []models.FieldMetadata{{APIName: "Email"}},
			},
		},
	}

	svc := makePCService(repo, metadata)
	svc.fieldProjectionAdmission = newBoundedFieldProjectionAdmission(1, 0)
	occupiedRelease, err := svc.fieldProjectionAdmission.acquire(context.Background())
	if err != nil {
		t.Fatalf("occupy FLS admission: %v", err)
	}
	defer occupiedRelease()
	ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
	defer cancel()
	perms, err := svc.ListEffectiveFieldPermissionsForUserObject(ctx, "user-1", "jira_admin", "Account")
	if err != nil {
		t.Fatalf("ListEffectiveFieldPermissionsForUserObject: %v", err)
	}
	if len(perms) != 2 {
		t.Fatalf("superuser Account projection = %+v; want exactly two Account fields", perms)
	}
	for _, perm := range perms {
		if perm.SobjectType != "Account" || !perm.PermissionsRead || !perm.PermissionsEdit {
			t.Fatalf("superuser bounded permission = %+v; want full Account access", perm)
		}
	}
	if got, _ := svc.fieldProjectionAdmission.snapshot(); got != 1 {
		t.Fatalf("superuser projection changed occupied FLS slots = %d, want 1", got)
	}

	canceledCtx, cancelNow := context.WithCancel(context.Background())
	cancelNow()
	if _, err := svc.ListEffectiveFieldPermissionsForUserObject(canceledCtx, "user-1", "jira_admin", "Account"); !errors.Is(err, context.Canceled) {
		t.Fatalf("pre-canceled superuser projection error = %v, want context canceled", err)
	}
}

func TestListEffectiveObjectPermissionsForSuperUserFallsBackWithoutSchemaSnapshot(t *testing.T) {
	ctx := context.Background()
	repo := &pcRepo{
		systemPerms: superUserCapabilityGrant(),
		effectiveList: []models.ObjectPermissions{{
			SobjectType:     "MyObject__c",
			PermissionsRead: true,
		}},
	}
	svc := makePCService(repo, &pcMetadataStub{})

	perms, err := svc.ListEffectiveObjectPermissionsForUser(ctx, "user-1", "jira_admin")
	if err != nil {
		t.Fatalf("ListEffectiveObjectPermissionsForUser: %v", err)
	}

	perm, ok := objectPermByType(perms, "MyObject__c")
	if !ok {
		t.Fatalf("missing fallback permission for MyObject__c: %+v", perms)
	}
	if !perm.PermissionsRead || perm.PermissionsCreate {
		t.Fatalf("fallback permission = %+v; want repo-backed read-only row", perm)
	}
}

func TestListEffectiveFieldPermissionsDoesNotInventFieldsWithoutSchemaSnapshot(t *testing.T) {
	ctx := context.Background()
	repo := &fieldEffectiveRepo{
		systemPerms: superUserCapabilityGrant(),
		perms: []models.FieldPermissions{{
			SobjectType:     "MyObject__c",
			Field:           "Name",
			PermissionsRead: true,
			PermissionsEdit: false,
		}},
	}
	svc := makePCService(repo, &pcMetadataStub{})

	perms, err := svc.ListEffectiveFieldPermissionsForUser(ctx, "user-1", "jira_admin")
	if err != nil {
		t.Fatalf("ListEffectiveFieldPermissionsForUser: %v", err)
	}

	if len(perms) != 0 {
		t.Fatalf("projected unknown schema fields from repository inputs: %+v", perms)
	}
}

type fieldEffectiveRepo struct {
	saveTestRepo
	systemPerms map[string]bool
	perms       []models.FieldPermissions
}

type projectionCountingMetadata struct {
	*pcMetadataStub
	getSchemaCalls  int
	getSchemasCalls int
}

func (m *projectionCountingMetadata) GetSchema(ctx context.Context, apiName string) *models.ObjectMetadata {
	m.getSchemaCalls++
	return m.pcMetadataStub.GetSchema(ctx, apiName)
}

func (m *projectionCountingMetadata) GetSchemas(ctx context.Context) []*models.ObjectMetadata {
	m.getSchemasCalls++
	return m.pcMetadataStub.GetSchemas(ctx)
}

type projectionCountingRepo struct {
	pcRepo
	systemPermissionLoads int
	fieldPermissionLoads  int
}

func (r *projectionCountingRepo) GetEffectiveSystemPermissions(ctx context.Context, userID, profileID string) (map[string]bool, error) {
	r.systemPermissionLoads++
	return r.pcRepo.GetEffectiveSystemPermissions(ctx, userID, profileID)
}

func (r *projectionCountingRepo) ListEffectiveFieldPermissionsForUser(context.Context, string, string) ([]models.FieldPermissions, error) {
	r.fieldPermissionLoads++
	return nil, nil
}

type cancelAfterErrChecksContext struct {
	context.Context
	cancel    context.CancelFunc
	remaining atomic.Int64
}

func newCancelAfterErrChecksContext(checks int64) *cancelAfterErrChecksContext {
	ctx, cancel := context.WithCancel(context.Background())
	result := &cancelAfterErrChecksContext{Context: ctx, cancel: cancel}
	result.remaining.Store(checks)
	return result
}

func (c *cancelAfterErrChecksContext) Err() error {
	if c.remaining.Add(-1) <= 0 {
		c.cancel()
	}
	return c.Context.Err()
}

func superUserCapabilityGrant() map[string]bool {
	return map[string]bool{string(constants.SuperUserCapability): true}
}

func (r *fieldEffectiveRepo) GetEffectiveSystemPermissions(context.Context, string, string) (map[string]bool, error) {
	out := make(map[string]bool, len(r.systemPerms))
	for key, value := range r.systemPerms {
		out[key] = value
	}
	return out, nil
}

func (r *fieldEffectiveRepo) ListEffectiveFieldPermissionsForUser(context.Context, string, string) ([]models.FieldPermissions, error) {
	out := make([]models.FieldPermissions, len(r.perms))
	copy(out, r.perms)
	return out, nil
}
