package permissions

import (
	"context"
	goerrors "errors"
	"testing"

	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/models"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
)

type contractAdminRepo struct {
	saveTestRepo
	effectiveSystemPerms map[string]bool
	effectiveCustomPerms map[string]bool
	effectiveObjectPerms []models.ObjectPermissions
	effectiveFieldPerms  []models.FieldPermissions
	effectiveAppPerms    map[string]models.ProfileAppVisibility
	effectiveNavPerms    map[string]models.ProfileTabSetting
	effectiveRecordTypes map[string]models.PermissionSetRecordTypeVisibility
	recordTypeReads      int
	storedObjectPerms    map[string]*models.ObjectPermissions
	effectiveFieldErr    error
	fullObjectReads      int
	pointObjectReads     int
	fullFieldReads       int
	boundedFieldReads    int
	appReads             int
	navigationReads      int
	userProfileID        string
	userProfileErr       error
	systemGrantsErr      error
	recordTypeGrantsErr  error
	customGrantsErr      error
}

func (m *contractAdminRepo) GetEffectiveSystemPermissions(context.Context, string, string) (map[string]bool, error) {
	if m.effectiveSystemPerms == nil {
		return map[string]bool{}, nil
	}
	return m.effectiveSystemPerms, nil
}

func (m *contractAdminRepo) GetEffectiveCustomPermissions(context.Context, string, string) (map[string]bool, error) {
	return m.effectiveCustomPerms, nil
}

func (m *contractAdminRepo) ListEffectiveObjectPermissionsForUser(context.Context, string, string) ([]models.ObjectPermissions, error) {
	m.fullObjectReads++
	return m.effectiveObjectPerms, nil
}

func (m *contractAdminRepo) LoadEffectiveObjectPermission(_ context.Context, _ *models.UserSession, objectAPIName string) (*models.ObjectPermissions, error) {
	m.pointObjectReads++
	if m.storedObjectPerms != nil {
		if perm, ok := m.storedObjectPerms[objectAPIName]; ok {
			cp := *perm
			return &cp, nil
		}
	}
	return m.saveTestRepo.LoadEffectiveObjectPermission(context.Background(), nil, objectAPIName)
}

func (m *contractAdminRepo) ListEffectiveFieldPermissionsForUser(context.Context, string, string) ([]models.FieldPermissions, error) {
	m.fullFieldReads++
	if m.effectiveFieldErr != nil {
		return nil, m.effectiveFieldErr
	}
	return m.effectiveFieldPerms, nil
}

func (m *contractAdminRepo) ListEffectiveFieldPermissionsForUserObject(
	_ context.Context,
	_ string,
	_ string,
	objectAPIName string,
) ([]models.FieldPermissions, error) {
	m.boundedFieldReads++
	if m.effectiveFieldErr != nil {
		return nil, m.effectiveFieldErr
	}
	var result []models.FieldPermissions
	for _, permission := range m.effectiveFieldPerms {
		if permission.SobjectType == objectAPIName {
			result = append(result, permission)
		}
	}
	return result, nil
}

func (m *contractAdminRepo) GetEffectiveAppVisibilities(context.Context, string, string) (map[string]models.ProfileAppVisibility, error) {
	m.appReads++
	return m.effectiveAppPerms, nil
}

func (m *contractAdminRepo) GetEffectiveTabSettings(context.Context, string, string) (map[string]models.ProfileTabSetting, error) {
	m.navigationReads++
	return m.effectiveNavPerms, nil
}

func (m *contractAdminRepo) GetEffectiveRecordTypeVisibilities(context.Context, string, string) (map[string]models.PermissionSetRecordTypeVisibility, error) {
	m.recordTypeReads++
	return m.effectiveRecordTypes, nil
}

func (m *contractAdminRepo) GetUserProfileID(context.Context, string) (string, error) {
	if m.userProfileErr != nil {
		return "", m.userProfileErr
	}
	if m.userProfileID == "" {
		return "target-profile", nil
	}
	return m.userProfileID, nil
}

func (m *contractAdminRepo) GetPermissionSetGrants(context.Context, string) (map[string]bool, error) {
	if m.systemGrantsErr != nil {
		return nil, m.systemGrantsErr
	}
	return map[string]bool{"ApiEnabled": true}, nil
}

func (m *contractAdminRepo) GetPermissionSetRecordTypeGrants(context.Context, string) ([]models.PermissionSetRecordTypeVisibility, error) {
	if m.recordTypeGrantsErr != nil {
		return nil, m.recordTypeGrantsErr
	}
	return []models.PermissionSetRecordTypeVisibility{{RecordTypeID: "rt-1", IsVisible: true}}, nil
}

func (m *contractAdminRepo) GetPermissionSetCustomPermGrants(context.Context, string) ([]models.PermissionSetCustomPermission, error) {
	if m.customGrantsErr != nil {
		return nil, m.customGrantsErr
	}
	return []models.PermissionSetCustomPermission{{CustomPermissionID: "cp-1", PermissionSetID: "ps-1"}}, nil
}

func TestContractPermissionAdminEffectivePermissionsTargetRequiresViewSetup(t *testing.T) {
	repo := &contractAdminRepo{}
	admin := NewContractPermissionAdmin(NewPermissionService(repo, nil, nil), nil)

	_, err := admin.EffectivePermissions(context.Background(), &models.UserSession{ID: "viewer", ProfileID: "profile-1"}, "target")

	assertPermissionAdminErrorKind(t, err, contract.PermissionAdminErrorForbidden)
}

func TestContractPermissionAdminEffectivePermissionsProjectsTargetUser(t *testing.T) {
	repo := &contractAdminRepo{
		effectiveSystemPerms: map[string]bool{string(constants.PermViewSetup): true},
		effectiveCustomPerms: map[string]bool{"CanExport": true},
		effectiveObjectPerms: []models.ObjectPermissions{
			{SobjectType: "ObjA", PermissionsRead: true, PermissionsCreate: true, PermissionsEdit: true},
			{SobjectType: "ObjB", PermissionsRead: true, PermissionsDelete: true, PermissionsViewAllRecords: true},
			{SobjectType: "ObjC", PermissionsCreate: true, PermissionsDelete: true, PermissionsModifyAllRecords: true},
		},
		effectiveFieldPerms: []models.FieldPermissions{{
			SobjectType:     "Account",
			Field:           "Secret__c",
			PermissionsRead: true,
			PermissionsEdit: false,
		}},
		effectiveAppPerms: map[string]models.ProfileAppVisibility{
			"Sales": {AppID: "Sales", IsVisible: true, IsDefault: true},
		},
		effectiveNavPerms: map[string]models.ProfileTabSetting{
			"Account": {TabName: "Account", Visibility: "DefaultOn"},
			"Contact": {TabName: "Contact", Visibility: "Hidden"},
		},
		effectiveRecordTypes: map[string]models.PermissionSetRecordTypeVisibility{
			"rt-visible": {RecordTypeID: "rt-visible", IsVisible: true},
			"rt-hidden":  {RecordTypeID: "rt-hidden", IsVisible: false},
		},
		userProfileID: "target-profile",
	}
	metadata := &flsMetadataStub{schemas: []*models.ObjectMetadata{{
		APIName: "Account", Fields: []models.FieldMetadata{{APIName: "Secret__c"}},
	}}}
	admin := NewContractPermissionAdmin(NewPermissionService(repo, metadata, nil), nil)

	got, err := admin.EffectivePermissions(context.Background(), &models.UserSession{ID: "viewer", ProfileID: "profile-1"}, "target")
	if err != nil {
		t.Fatalf("EffectivePermissions() error = %v, want nil", err)
	}
	if !got.SystemPermissions[string(constants.PermViewSetup)] {
		t.Fatalf("system permissions = %+v, want ViewSetup", got.SystemPermissions)
	}
	if !got.CustomPermissions["CanExport"] {
		t.Fatalf("custom permissions = %+v, want CanExport", got.CustomPermissions)
	}
	// Producer boolean mapping must be field-faithful: the dialect wire golden uses
	// a fake, so this is the ONLY guard for the engine models.* -> neutral projection
	// mapping. Unique per-field signatures across ObjA/ObjB/ObjC catch any
	// Permissions*<->Can* swap in the producer (e.g. CanDelete <- PermissionsCreate).
	wantObj := []contract.ObjectAccessProjection{
		{ResourceType: "ObjA", CanRead: true, CanCreate: true, CanEdit: true},
		{ResourceType: "ObjB", CanRead: true, CanDelete: true, CanViewAll: true},
		{ResourceType: "ObjC", CanCreate: true, CanDelete: true, CanModifyAll: true},
	}
	if len(got.ObjectPermissions) != len(wantObj) {
		t.Fatalf("object permissions = %+v, want %+v", got.ObjectPermissions, wantObj)
	}
	for i := range wantObj {
		if got.ObjectPermissions[i] != wantObj[i] {
			t.Fatalf("object projection[%d] = %+v, want %+v (producer boolean mis-map?)", i, got.ObjectPermissions[i], wantObj[i])
		}
	}
	wantField := contract.FieldAccessProjection{ResourceType: "Account", FieldName: "Secret__c", CanRead: true, CanEdit: false}
	if len(got.FieldPermissions) != 1 || got.FieldPermissions[0] != wantField {
		t.Fatalf("field permissions = %+v, want %+v", got.FieldPermissions, wantField)
	}
	if len(got.ApplicationPermissions) != 1 ||
		got.ApplicationPermissions[0] != (contract.ApplicationAccessProjection{
			ApplicationID: "Sales", CanView: true, IsDefault: true,
		}) {
		t.Fatalf("application permissions = %+v", got.ApplicationPermissions)
	}
	if len(got.NavigationPermissions) != 2 {
		t.Fatalf("navigation permissions = %+v", got.NavigationPermissions)
	}
	byNavigationKey := make(map[string]bool, len(got.NavigationPermissions))
	for _, permission := range got.NavigationPermissions {
		byNavigationKey[permission.NavigationKey] = permission.CanView
	}
	if !byNavigationKey["Account"] || byNavigationKey["Contact"] {
		t.Fatalf("navigation permissions = %+v", got.NavigationPermissions)
	}
	if len(got.RecordTypePermissions) != 2 ||
		!got.RecordTypePermissions["rt-visible"] || got.RecordTypePermissions["rt-hidden"] {
		t.Fatalf("record type permissions = %+v", got.RecordTypePermissions)
	}
	if repo.recordTypeReads != 1 {
		t.Fatalf("record type reads = %d, want 1", repo.recordTypeReads)
	}
}

func TestContractPermissionAdminSelectedSkipsCompleteAndUnrequestedReads(t *testing.T) {
	accountSchema := &models.ObjectMetadata{
		APIName: "Account", SharingModel: constants.SharingModelPublicReadWrite,
		Fields: []models.FieldMetadata{{APIName: "Name", Type: constants.FieldTypeString}},
	}
	repo := &contractAdminRepo{
		storedObjectPerms: map[string]*models.ObjectPermissions{
			"Account": {SobjectType: "Account", PermissionsRead: true},
		},
		effectiveFieldPerms: []models.FieldPermissions{{
			SobjectType: "Account", Field: "Name", PermissionsRead: true,
		}},
		effectiveAppPerms: map[string]models.ProfileAppVisibility{
			"Sales": {AppID: "Sales", IsVisible: true},
		},
		effectiveNavPerms: map[string]models.ProfileTabSetting{
			"Account": {TabName: "Account", Visibility: "DefaultOn"},
		},
	}
	admin := NewContractPermissionAdmin(
		NewPermissionService(repo, &flsMetadataStub{schema: accountSchema}, nil), nil,
	)
	selection := permissionsemantic.Selection{
		Sections: []permissionsemantic.Section{
			permissionsemantic.SectionObjectPermissions,
			permissionsemantic.SectionFieldPermissions,
			permissionsemantic.SectionRecordTypePermissions,
		},
		ResourceTypes: []string{"Account"},
	}
	got, err := admin.EffectivePermissionsSelected(
		context.Background(),
		&models.UserSession{ID: "viewer", ProfileID: "profile-1"},
		"",
		selection,
	)
	if err != nil {
		t.Fatal(err)
	}
	if len(got.SystemPermissions) != 0 || len(got.CustomPermissions) != 0 ||
		len(got.ApplicationPermissions) != 0 || len(got.NavigationPermissions) != 0 ||
		len(got.ObjectPermissions) != 1 || got.ObjectPermissions[0].ResourceType != "Account" ||
		len(got.FieldPermissions) != 1 || got.FieldPermissions[0].ResourceType != "Account" {
		t.Fatalf("selected permissions = %#v", got)
	}
	if repo.fullObjectReads != 0 || repo.fullFieldReads != 0 ||
		repo.appReads != 0 || repo.navigationReads != 0 ||
		repo.pointObjectReads != 1 || repo.boundedFieldReads != 1 {
		t.Fatalf(
			"reads full-object/full-field/app/nav/point-object/bounded-field = %d/%d/%d/%d/%d/%d",
			repo.fullObjectReads, repo.fullFieldReads, repo.appReads,
			repo.navigationReads, repo.pointObjectReads, repo.boundedFieldReads,
		)
	}
}

func TestContractPermissionAdminSuperUserDoesNotBorrowRecordTypeRows(t *testing.T) {
	repo := &contractAdminRepo{
		effectiveSystemPerms: map[string]bool{
			string(constants.SuperUserCapability): true,
		},
		effectiveRecordTypes: map[string]models.PermissionSetRecordTypeVisibility{
			"rt-hidden": {RecordTypeID: "rt-hidden", IsVisible: false},
		},
	}
	admin := NewContractPermissionAdmin(NewPermissionService(repo, &flsMetadataStub{}, nil), nil)

	got, err := admin.EffectivePermissions(
		context.Background(),
		&models.UserSession{ID: "admin", ProfileID: "admin-profile"},
		"",
	)
	if err != nil {
		t.Fatal(err)
	}
	if repo.recordTypeReads != 0 || got.RecordTypePermissions != nil {
		t.Fatalf(
			"superuser projection = reads %d permissions %+v, want no repository visibility dependency",
			repo.recordTypeReads,
			got.RecordTypePermissions,
		)
	}
}

func TestContractPermissionAdminProjectsUserDeactivationCapability(t *testing.T) {
	userSchema := &models.ObjectMetadata{
		APIName:                 constants.TableUser,
		RequiredWritePermission: string(constants.PermManageUsers),
		AccessPolicy: &models.ObjectAccessPolicy{
			WriteValidation: &models.RecordWriteValidationAccessPolicy{
				Validators: []models.RecordWriteValidationPolicy{{
					Phase:      "before_delete",
					Capability: userDeleteLifecycleWriteCapability,
				}},
			},
		},
	}
	repo := &contractAdminRepo{
		effectiveSystemPerms: map[string]bool{
			string(constants.PermManageUsers): true,
			string(constants.PermViewSetup):   true,
		},
		effectiveObjectPerms: []models.ObjectPermissions{{
			SobjectType:       constants.TableUser,
			PermissionsRead:   true,
			PermissionsDelete: false,
		}},
		storedObjectPerms: map[string]*models.ObjectPermissions{
			constants.TableUser: {
				SobjectType:       constants.TableUser,
				PermissionsRead:   true,
				PermissionsDelete: true,
			},
		},
		userProfileID: "target-profile",
	}
	admin := NewContractPermissionAdmin(NewPermissionService(repo, &flsMetadataStub{schema: userSchema}, nil), nil)

	got, err := admin.EffectivePermissions(context.Background(), &models.UserSession{ID: "viewer", ProfileID: "profile-viewer"}, "target")
	if err != nil {
		t.Fatalf("EffectivePermissions() error = %v, want nil", err)
	}
	if len(got.ObjectPermissions) != 1 {
		t.Fatalf("object permissions = %+v, want one User row", got.ObjectPermissions)
	}
	userAccess := got.ObjectPermissions[0]
	if userAccess.CanDelete {
		t.Fatalf("CanDelete = true, want false for generic User delete")
	}
	if !userAccess.CanDeactivate {
		t.Fatalf("CanDeactivate = false, want true from stored Delete User grant")
	}
}

func TestContractPermissionAdminEffectivePermissionsFieldErrorIsSanitized(t *testing.T) {
	repo := &contractAdminRepo{
		effectiveFieldErr: appErrors.NewInternalError(
			"failed to query effective field permissions",
			goerrors.New("driver: SELECT Secret__c FROM FieldPermissions"),
		),
	}
	admin := NewContractPermissionAdmin(NewPermissionService(repo, nil, nil), nil)

	_, err := admin.EffectivePermissions(context.Background(), &models.UserSession{ID: "viewer", ProfileID: "profile-1"}, "")

	adminErr := assertPermissionAdminErrorKind(t, err, contract.PermissionAdminErrorSanitizedInternal)
	if adminErr.Message != "An internal server error occurred. Contact your system administrator." {
		t.Fatalf("message = %q, want sanitized internal message", adminErr.Message)
	}
}

func TestContractPermissionAdminGrantReadsPreserveNotFoundClassification(t *testing.T) {
	missing := appErrors.NewNotFoundError("PermissionSet", "ps-missing")
	repo := &contractAdminRepo{
		systemGrantsErr:     missing,
		recordTypeGrantsErr: missing,
		customGrantsErr:     missing,
	}
	admin := NewContractPermissionAdmin(NewPermissionService(repo, nil, nil), nil)

	if _, err := admin.GetPermissionSetSystemGrants(context.Background(), "ps-missing"); err != nil {
		assertPermissionAdminErrorKind(t, err, contract.PermissionAdminErrorNotFound)
	} else {
		t.Fatal("GetPermissionSetSystemGrants() error = nil, want not_found")
	}
	if _, err := admin.GetPermissionSetRecordTypeGrants(context.Background(), "ps-missing"); err != nil {
		assertPermissionAdminErrorKind(t, err, contract.PermissionAdminErrorNotFound)
	} else {
		t.Fatal("GetPermissionSetRecordTypeGrants() error = nil, want not_found")
	}
	if _, err := admin.GetPermissionSetCustomPermissionGrants(context.Background(), "ps-missing"); err != nil {
		assertPermissionAdminErrorKind(t, err, contract.PermissionAdminErrorNotFound)
	} else {
		t.Fatal("GetPermissionSetCustomPermissionGrants() error = nil, want not_found")
	}
}

func TestContractPermissionAdminSaveRecordTypeGrantsUnknownRecordTypeIsInvalidField(t *testing.T) {
	repo := &contractAdminRepo{
		saveTestRepo: saveTestRepo{
			recordTypes: []ports.RecordTypeCatalogEntry{{RecordTypeID: "rt-1", ObjectAPIName: "Account", Name: "Business", Label: "Business", IsActive: true}},
		},
	}
	svc := NewPermissionService(repo, nil, nil)
	adminSvc := NewAdminService(svc, &recordingPermissionAdminTx{}, AdminServiceOptions{})
	admin := NewContractPermissionAdmin(svc, adminSvc)

	err := admin.SavePermissionSetRecordTypeGrants(context.Background(), "ps-1", map[string]contract.PermissionRecordTypeGrantChange{
		"rt-missing": {IsVisible: true},
	})

	assertPermissionAdminErrorKind(t, err, contract.PermissionAdminErrorInvalidField)
}

func TestContractPermissionAdminRejectsSystemProfileOwnedSetAsForbidden(t *testing.T) {
	systemProfileOwnedLookup := func(_ context.Context, tableName, id string) (models.SObject, error) {
		if tableName == constants.TablePermissionSet && id == "ps-system" {
			return models.SObject{
				constants.FieldID: "ps-system",
				constants.FieldPermissionSet_IsOwnedByProfile: true,
				constants.FieldPermissionSet_ProfileID:        "00E-sys-profile",
			}, nil
		}
		if tableName == constants.TableProfile && id == "00E-sys-profile" {
			return models.SObject{
				constants.FieldID:               "00E-sys-profile",
				constants.FieldProfile_IsSystem: true,
			}, nil
		}
		return nil, nil
	}
	repo := &contractAdminRepo{
		saveTestRepo: saveTestRepo{
			systemPerms:     []models.SystemPermission{{APIName: "ApiEnabled", IsActive: true}},
			getRecordByIDFn: systemProfileOwnedLookup,
		},
	}
	svc := NewPermissionService(repo, nil, nil)
	adminSvc := NewAdminService(svc, &recordingPermissionAdminTx{}, AdminServiceOptions{})
	admin := NewContractPermissionAdmin(svc, adminSvc)

	err := admin.SavePermissionSetSystemGrants(context.Background(), "ps-system", map[string]bool{"ApiEnabled": true}, &models.UserSession{ID: "user-1"})

	assertPermissionAdminErrorKind(t, err, contract.PermissionAdminErrorForbidden)
}

func TestContractPermissionAdminMutationKeepsInternalBeforeBusinessClassification(t *testing.T) {
	err := appErrors.NewInternalError(
		"rollback failed",
		&PermissionSetNotFoundError{PermissionSetID: "ps-1"},
	)

	adminErr := assertPermissionAdminErrorKind(t, permissionAdminMutationError(err), contract.PermissionAdminErrorServer)
	if adminErr.Message == "" {
		t.Fatal("server error message must be preserved")
	}
}

func assertPermissionAdminErrorKind(t *testing.T, err error, want contract.PermissionAdminErrorKind) *contract.PermissionAdminError {
	t.Helper()
	var adminErr *contract.PermissionAdminError
	if !goerrors.As(err, &adminErr) {
		t.Fatalf("error = %T %[1]v, want *contract.PermissionAdminError", err)
	}
	if adminErr.Kind != want {
		t.Fatalf("error kind = %s, want %s (message=%q)", adminErr.Kind, want, adminErr.Message)
	}
	return adminErr
}
