package dialectsfcrm

import (
	"bytes"
	"context"
	"errors"
	"log"
	"net/http"
	"strconv"
	"strings"
	"testing"

	"github.com/nexuscrm/dialect-sf-crm/wire"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
)

func TestFolderErrorProjectionDoesNotLeakPlainErrors(t *testing.T) {
	tests := []struct {
		name       string
		deps       dialect.Deps
		wantStatus int
		wantCode   string
		forbidden  string
		wantLog    string
	}{
		{
			name:       "access denied is not found",
			deps:       folderDeps(&folderRecordsStub{}, &folderQueryStub{}, &folderAccessStub{}, nil, folderPolicyStub{}),
			wantStatus: http.StatusNotFound,
			wantCode:   wire.SFCodeNotFound,
		},
		{
			name:       "missing deps fail closed",
			deps:       dialect.Deps{},
			wantStatus: http.StatusInternalServerError,
			wantCode:   wire.SFCodeInternalError,
		},
		{
			name: "plain retrieve error is sanitized",
			deps: folderDeps(&folderRecordsStub{
				retrieveErr: errors.New("driver failed: select * from FolderShare secrets"),
			}, &folderQueryStub{}, &folderAccessStub{allowView: true}, nil, folderPolicyStub{}),
			wantStatus: http.StatusInternalServerError,
			wantCode:   wire.SFCodeInternalError,
			forbidden:  "FolderShare secrets",
			wantLog:    "FolderShare secrets",
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			logOutput := captureFolderLogs(t)
			resp, err := GetFolderNeutralHandler(context.Background(), &dialect.Request{
				Params: map[string]string{"id": "00l000000000010AAA"},
			}, tc.deps)
			if err != nil {
				t.Fatalf("handler returned error: %v", err)
			}
			assertSFError(t, resp, tc.wantStatus, tc.wantCode)
			if tc.forbidden != "" && strings.Contains(marshalJSON(t, resp.Body), tc.forbidden) {
				t.Fatalf("body leaked %q: %s", tc.forbidden, marshalJSON(t, resp.Body))
			}
			if tc.wantLog != "" && !strings.Contains(logOutput.String(), tc.wantLog) {
				t.Fatalf("logs = %q, want %q", logOutput.String(), tc.wantLog)
			}
		})
	}
}

func TestFolderChildrenProjectsV63Envelope(t *testing.T) {
	parentID := "00l000000000010AAA"
	query := &folderQueryStub{result: &contract.QueryResult{Records: []contract.Record{
		{
			constants.FieldReportFolder_ID:            "00l000000000011AAA",
			constants.FieldReportFolder_Name:          "Child Reports",
			constants.FieldReportFolder_DeveloperName: "Child_Reports",
			constants.FieldReportFolder_Type:          "Report",
			constants.FieldReportFolder_AccessType:    "Hidden",
			constants.FieldReportFolder_ParentID:      parentID,
		},
		{
			constants.FieldReportFolder_ID:            "00l000000000012AAA",
			constants.FieldReportFolder_Name:          "Second Reports",
			constants.FieldReportFolder_DeveloperName: "Second_Reports",
			constants.FieldReportFolder_Type:          "Report",
			constants.FieldReportFolder_AccessType:    "Hidden",
			constants.FieldReportFolder_ParentID:      parentID,
		},
	}}}

	resp, err := GetFolderChildrenNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": parentID},
		Query:   map[string][]string{"pageSize": {"1"}},
	}, folderDeps(&folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + parentID: folderRecord(parentID, "Parent Reports", "Report", "Shared"),
	}}, query, &folderAccessStub{allowView: true}, nil, folderPolicyStub{}))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}

	assertBody(t, "folder children", resp.Body, "", `{"folders":[{"id":"00l000000000011AAA","name":"Child Reports","label":"Child Reports","developerName":"Child_Reports","type":"Report","accessType":"Hidden","parentFolderId":"00l000000000010AAA"}],"totalSize":2,"url":"/services/data/v63.0/folders/00l000000000010AAA/children?page=0\u0026pageSize=1"}`)
}

func TestFolderSharesProjectV63WireShape(t *testing.T) {
	folderID := "00l000000000010AAA"
	shareID := "0F9000000000001AAA"
	sharedToID := "005000000000002AAA"
	userRecord := contract.Record{
		constants.FieldID:        sharedToID,
		constants.FieldFirstName: "Ada",
		constants.FieldLastName:  "Lovelace",
		constants.FieldUsername:  "ada@example.invalid",
	}
	resourceAccess := &folderResourceAccessStub{grants: []contract.ResourceAccessGrant{{
		ID:            shareID,
		ResourceType:  folderResourceAccessType,
		ResourceID:    folderID,
		PrincipalID:   sharedToID,
		PrincipalType: "User",
		AccessLevel:   "Manage",
	}}}
	deps := folderDeps(&folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Shared"),
	}}, &folderQueryStub{results: map[string]*contract.QueryResult{
		constants.TableUser: {Records: []contract.Record{userRecord}},
	}}, &folderAccessStub{allowView: true}, resourceAccess, folderPolicyStub{})

	listResp, err := ListFolderSharesNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID},
	}, deps)
	if err != nil {
		t.Fatalf("list handler returned error: %v", err)
	}
	if listResp.Status != http.StatusOK {
		t.Fatalf("list status = %d, want 200; body=%#v", listResp.Status, listResp.Body)
	}
	wantShare := `{"shareId":"0F9000000000001AAA","accessType":"Manage","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Ada Lovelace","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/0F9000000000001AAA"}`
	assertBody(t, "folder shares list", listResp.Body, "", `{"shares":[`+wantShare+`]}`)

	singleResp, err := GetFolderShareNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID, "shareId": shareID},
	}, deps)
	if err != nil {
		t.Fatalf("single handler returned error: %v", err)
	}
	if singleResp.Status != http.StatusOK {
		t.Fatalf("single status = %d, want 200; body=%#v", singleResp.Status, singleResp.Body)
	}
	assertBody(t, "folder share single", singleResp.Body, "", wantShare)
	if resourceAccess.listCalls != 1 {
		t.Fatalf("ListResourceAccessGrants calls = %d, want 1", resourceAccess.listCalls)
	}
	if resourceAccess.getCalls != 1 {
		t.Fatalf("GetResourceAccessGrant calls = %d, want 1", resourceAccess.getCalls)
	}
	if resourceAccess.getReq.ResourceType != folderResourceAccessType || resourceAccess.getReq.GrantID != shareID {
		t.Fatalf("GetResourceAccessGrant req = %#v, want resourceType=%q grantID=%q", resourceAccess.getReq, folderResourceAccessType, shareID)
	}
}

func TestFolderSharesListUsesResourceAccessSeam(t *testing.T) {
	folderID := "00l000000000010AAA"
	query := &folderQueryStub{queryFn: func(req queryspec.QuerySpec) (*contract.QueryResult, error) {
		if req.ObjectAPIName == constants.TableFolderShare {
			return nil, errors.New("query must not be used for folder share listing")
		}
		if req.ObjectAPIName == constants.TableUser {
			return &contract.QueryResult{Records: []contract.Record{
				{
					constants.FieldID:        "005000000000002AAA",
					constants.FieldUsername:  "ada@example.com",
					constants.FieldFirstName: "Ada",
					constants.FieldLastName:  "Lovelace",
				},
			}}, nil
		}
		return &contract.QueryResult{}, nil
	}}
	resourceAccess := &folderResourceAccessStub{
		grants: []contract.ResourceAccessGrant{
			{
				ID:            "0F9000000000001AAA",
				ResourceType:  folderResourceAccessType,
				ResourceID:    folderID,
				PrincipalID:   "ALL_INTERNAL",
				PrincipalType: "Organization",
				AccessLevel:   "View",
			},
			{
				ID:            "0F9000000000002AAA",
				ResourceType:  folderResourceAccessType,
				ResourceID:    folderID,
				PrincipalID:   "005000000000002AAA",
				PrincipalType: "User",
				AccessLevel:   "Manage",
			},
		},
	}
	rt := &folderRouteDeps{
		folderAccess:   &folderAccessStub{allowView: true},
		resourceAccess: resourceAccess,
		query:          query,
	}

	shares, err := rt.listFolderShares(context.Background(), folderID, folderTestUser())
	if err != nil {
		t.Fatalf("listFolderShares returned error: %v", err)
	}
	if resourceAccess.listReq.ResourceType != folderResourceAccessType || resourceAccess.listReq.ResourceID != folderID {
		t.Fatalf("ListResourceAccessGrants req = %#v, want resourceType=%q resourceID=%q", resourceAccess.listReq, folderResourceAccessType, folderID)
	}
	if resourceAccess.listCalls != 1 {
		t.Fatalf("ListResourceAccessGrants calls = %d, want 1", resourceAccess.listCalls)
	}
	for _, req := range query.reqs {
		if req.ObjectAPIName == constants.TableFolderShare {
			t.Fatalf("listFolderShares queried FolderShare through generic Query: %#v", req)
		}
	}
	want := []folderShareModel{
		{
			ID:              "0F9000000000001AAA",
			FolderID:        folderID,
			SharedToID:      "ALL_INTERNAL",
			SharedToType:    "Organization",
			AccessLevel:     "View",
			SharedWithLabel: "All Internal Users",
		},
		{
			ID:              "0F9000000000002AAA",
			FolderID:        folderID,
			SharedToID:      "005000000000002AAA",
			SharedToType:    "User",
			AccessLevel:     "Manage",
			SharedWithLabel: "Ada Lovelace",
		},
	}
	if len(shares) != len(want) {
		t.Fatalf("shares len = %d, want %d", len(shares), len(want))
	}
	for i := range want {
		if shares[i] != want[i] {
			t.Fatalf("share[%d] = %#v, want %#v", i, shares[i], want[i])
		}
	}
}

func TestListFoldersUsesResourceVisibilityAndEnsuresPersonalFolders(t *testing.T) {
	records := &folderRecordsStub{}
	query := &folderQueryStub{queryFn: func(req queryspec.QuerySpec) (*contract.QueryResult, error) {
		if req.Limit == 1 {
			return &contract.QueryResult{}, nil
		}
		return &contract.QueryResult{Records: []contract.Record{
			folderRecord("00l000000000011AAA", "Visible Reports", "Report", "Shared"),
			folderRecord("00l000000000012AAA", "Hidden Reports", "Report", "Shared"),
			folderRecord("00l000000000013AAA", "Dashboards", "Dashboard", "Shared"),
		}}, nil
	}}
	resourceAccess := &folderResourceAccessStub{visibleIDs: []string{"00l000000000011AAA", "00l000000000013AAA"}}

	resp, err := ListFoldersNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Query:   map[string][]string{"filterGroup": {"Reports"}},
	}, folderDeps(records, query, &folderAccessStub{allowView: true}, resourceAccess, folderPolicyStub{}))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	assertBody(t, "folder list", resp.Body, "", `{"folders":[{"id":"00l000000000011AAA","name":"Visible Reports","label":"Visible Reports","developerName":"Visible_Reports","type":"Report","accessType":"Shared"}],"hasNextPage":false}`)
	if len(records.inserted) != 2 {
		t.Fatalf("personal folder inserts = %d, want 2", len(records.inserted))
	}
	if got := records.inserted[0][constants.FieldReportFolder_Name]; got != "My Personal Custom Reports" {
		t.Fatalf("first personal folder name = %v", got)
	}
}

func TestListFoldersFilterGroupUsesPersistedTypeBeforeResponseDefault(t *testing.T) {
	legacy := folderRecord("00l000000000021AAA", "Legacy Untyped Folder", "", "Shared")
	delete(legacy, constants.FieldReportFolder_Type)
	explicit := folderRecord("00l000000000022AAA", "Explicit Reports", "Report", "Shared")
	query := &folderQueryStub{result: &contract.QueryResult{Records: []contract.Record{legacy, explicit}}}

	model := folderFromRecord(legacy)
	if model.RawType != "" || model.Type != "Report" {
		t.Fatalf("folderFromRecord raw/default type = %q/%q, want empty/Report", model.RawType, model.Type)
	}

	resp, err := ListFoldersNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Query:   map[string][]string{"filterGroup": {"Reports"}},
	}, folderDeps(&folderRecordsStub{}, query, &folderAccessStub{allowView: true}, &folderResourceAccessStub{}, folderPolicyStub{}))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	assertBody(t, "folder list filter raw type", resp.Body, "", `{"folders":[{"id":"00l000000000022AAA","name":"Explicit Reports","label":"Explicit Reports","developerName":"Explicit_Reports","type":"Report","accessType":"Shared"}],"hasNextPage":false}`)
}

func TestCreateFolderProjectsNeutralWriteValidationErrors(t *testing.T) {
	tests := []struct {
		name string
		err  error
		want string
	}{
		{
			name: "field write denied",
			err:  folderFieldWriteDeniedError{},
			want: `[{"errorCode":"INVALID_FIELD_FOR_INSERT_UPDATE","message":"fields denied","fields":["Name","DeveloperName"]}]`,
		},
		{
			name: "validation rule",
			err:  folderValidationRuleError{},
			want: `[{"errorCode":"FIELD_CUSTOM_VALIDATION_EXCEPTION","message":"Folder rule failed"}]`,
		},
		{
			name: "validation field",
			err:  NewValidationError("Name", "Bad folder name"),
			want: `[{"errorCode":"INVALID_FIELD","message":"Bad folder name","fields":["Name"]}]`,
		},
	}

	policy := folderPolicyStub{
		string(constants.PermCreateCustomizeReports):    true,
		string(constants.PermManageReportsInPubFolders): true,
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			records := &folderRecordsStub{insertFn: func(string, contract.Record) (contract.Record, error) {
				return nil, tc.err
			}}
			resp, err := CreateFolderNeutralHandler(context.Background(), &dialect.Request{
				Version: "v63.0",
				User:    folderTestUser(),
				Body:    []byte(`{"name":"Reports","type":"Report","accessType":"Hidden"}`),
			}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, &folderResourceAccessStub{}, policy))
			if err != nil {
				t.Fatalf("handler returned error: %v", err)
			}
			if resp.Status != http.StatusBadRequest {
				t.Fatalf("status = %d, want 400; body=%#v", resp.Status, resp.Body)
			}
			assertBody(t, "folder write validation "+tc.name, resp.Body, "", tc.want)
		})
	}
}

func TestCreatePublicFolderEnsuresOrganizationGrant(t *testing.T) {
	folderID := "00l000000000099AAA"
	records := &folderRecordsStub{insertFn: func(object string, data contract.Record) (contract.Record, error) {
		if object != constants.TableReportFolder {
			t.Fatalf("insert object = %q, want ReportFolder", object)
		}
		out := contract.Record{}
		for key, value := range data {
			out[key] = value
		}
		out[constants.FieldReportFolder_ID] = folderID
		return out, nil
	}}
	resourceAccess := &folderResourceAccessStub{}
	policy := folderPolicyStub{
		string(constants.PermCreateCustomizeReports):    true,
		string(constants.PermManageReportsInPubFolders): true,
	}

	resp, err := CreateFolderNeutralHandler(context.Background(), &dialect.Request{
		User: folderTestUser(),
		Body: []byte(`{"name":"Team Reports","type":"Report","accessType":"Public"}`),
	}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusCreated {
		t.Fatalf("status = %d, want 201; body=%#v", resp.Status, resp.Body)
	}
	if resourceAccess.ensureCalls != 1 {
		t.Fatalf("EnsureResourceAccessGrant calls = %d, want 1", resourceAccess.ensureCalls)
	}
	if resourceAccess.ensureReq.ResourceType != folderResourceAccessType || resourceAccess.ensureReq.ResourceID != folderID {
		t.Fatalf("ensure request = %#v", resourceAccess.ensureReq)
	}
	if grant := resourceAccess.ensureReq.Grant; grant.PrincipalID != "ALL_INTERNAL" || grant.PrincipalType != "Organization" || grant.AccessLevel != "View" {
		t.Fatalf("ensure grant = %#v, want Organization View ALL_INTERNAL", grant)
	}
}

func TestCreatePublicFolderShareSyncFailureUsesGenericInternalError(t *testing.T) {
	folderID := "00l000000000099AAA"
	records := &folderRecordsStub{insertFn: func(object string, data contract.Record) (contract.Record, error) {
		out := contract.Record{}
		for key, value := range data {
			out[key] = value
		}
		out[constants.FieldReportFolder_ID] = folderID
		return out, nil
	}}
	resourceAccess := &folderResourceAccessStub{ensureErr: errors.New("share insert leaked detail")}
	policy := folderPolicyStub{
		string(constants.PermCreateCustomizeReports):    true,
		string(constants.PermManageReportsInPubFolders): true,
	}
	logs := captureFolderLogs(t)

	resp, err := CreateFolderNeutralHandler(context.Background(), &dialect.Request{
		User: folderTestUser(),
		Body: []byte(`{"name":"Team Reports","type":"Report","accessType":"Public"}`),
	}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusInternalServerError {
		t.Fatalf("status = %d, want 500; body=%#v", resp.Status, resp.Body)
	}
	want := mustJSON(t, wire.SFGenericInternalErrorResponse())
	assertBody(t, "folder create share sync failure", resp.Body, want, want)
	body := mustJSON(t, resp.Body)
	if strings.Contains(body, "SHARE_SYNC_FAILED") || strings.Contains(body, "share insert leaked detail") || strings.Contains(body, "Organization share") {
		t.Fatalf("internal share-sync detail leaked in body: %s", body)
	}
	if got := logs.String(); !strings.Contains(got, "share insert leaked detail") {
		t.Fatalf("logs = %q, want wrapped cause", got)
	}
}

func TestUpdateFolderShareSyncFailureUsesGenericInternalError(t *testing.T) {
	folderID := "00l000000000010AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Public"),
	}}
	resourceAccess := &folderResourceAccessStub{deleteByTypeErr: errors.New("delete org share leaked detail")}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}
	logs := captureFolderLogs(t)

	resp, err := UpdateFolderNeutralHandler(context.Background(), &dialect.Request{
		User:   folderTestUser(),
		Params: map[string]string{"id": folderID},
		Body:   []byte(`{"accessType":"Shared"}`),
	}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusInternalServerError {
		t.Fatalf("status = %d, want 500; body=%#v", resp.Status, resp.Body)
	}
	want := mustJSON(t, wire.SFGenericInternalErrorResponse())
	assertBody(t, "folder update share sync failure", resp.Body, want, want)
	body := mustJSON(t, resp.Body)
	if strings.Contains(body, "SHARE_SYNC_FAILED") || strings.Contains(body, "delete org share leaked detail") || strings.Contains(body, "sync folder sharing") {
		t.Fatalf("internal share-sync detail leaked in body: %s", body)
	}
	if got := logs.String(); !strings.Contains(got, "delete org share leaked detail") {
		t.Fatalf("logs = %q, want wrapped cause", got)
	}
}

func TestAddFolderSharesProjectsV63ShareResponse(t *testing.T) {
	folderID := "00l000000000010AAA"
	sharedToID := "005000000000002AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Shared"),
	}}
	query := &folderQueryStub{results: map[string]*contract.QueryResult{
		constants.TableUser: {Records: []contract.Record{{
			constants.FieldID:        sharedToID,
			constants.FieldFirstName: "Ada",
			constants.FieldLastName:  "Lovelace",
			constants.FieldUsername:  "ada@example.invalid",
		}}},
	}}
	resourceAccess := &folderResourceAccessStub{}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	resp, err := AddFolderSharesNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID},
		Body:    []byte(`{"shares":[{"sharedToId":"005000000000002AAA","sharedToType":"User","accessLevel":"manage"}]}`),
	}, folderDeps(records, query, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusCreated {
		t.Fatalf("status = %d, want 201; body=%#v", resp.Status, resp.Body)
	}
	assertBody(t, "folder share add", resp.Body, "", `{"shares":[{"shareId":"share-created-1","accessType":"Manage","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Ada Lovelace","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-created-1"}]}`)
}

func TestAddFolderSharesSortsMutationResponseByShareID(t *testing.T) {
	folderID := "00l000000000010AAA"
	firstUserID := "005000000000002AAA"
	secondUserID := "005000000000003AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Shared"),
	}}
	query := &folderQueryStub{results: map[string]*contract.QueryResult{
		constants.TableUser: {Records: []contract.Record{
			{
				constants.FieldID:        firstUserID,
				constants.FieldFirstName: "Ada",
				constants.FieldLastName:  "Lovelace",
				constants.FieldUsername:  "ada@example.invalid",
			},
			{
				constants.FieldID:        secondUserID,
				constants.FieldFirstName: "Grace",
				constants.FieldLastName:  "Hopper",
				constants.FieldUsername:  "grace@example.invalid",
			},
		}},
	}}
	resourceAccess := &folderResourceAccessStub{createIDs: []string{"share-z", "share-a"}}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	resp, err := AddFolderSharesNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID},
		Body:    []byte(`{"shares":[{"sharedToId":"005000000000002AAA","sharedToType":"User","accessLevel":"view"},{"sharedToId":"005000000000003AAA","sharedToType":"User","accessLevel":"manage"}]}`),
	}, folderDeps(records, query, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusCreated {
		t.Fatalf("status = %d, want 201; body=%#v", resp.Status, resp.Body)
	}
	assertBody(t, "folder share add sorted", resp.Body, "", `{"shares":[{"shareId":"share-a","accessType":"Manage","shareType":"User","sharedWithId":"005000000000003AAA","sharedWithLabel":"Grace Hopper","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-a"},{"shareId":"share-z","accessType":"View","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Ada Lovelace","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-z"}]}`)
}

func TestFolderShareMutationsRejectOrganizationAuthoring(t *testing.T) {
	folderID := "00l000000000010AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Shared"),
	}}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	tests := []struct {
		name string
		call func(*folderResourceAccessStub) (*dialect.Response, error)
	}{
		{
			name: "add organization",
			call: func(resourceAccess *folderResourceAccessStub) (*dialect.Response, error) {
				return AddFolderSharesNeutralHandler(context.Background(), &dialect.Request{
					Version: "v63.0",
					User:    folderTestUser(),
					Params:  map[string]string{"id": folderID},
					Body:    []byte(`{"shares":[{"sharedToId":"ALL_INTERNAL","sharedToType":"Organization","accessLevel":"view"}]}`),
				}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, policy))
			},
		},
		{
			name: "replace organization",
			call: func(resourceAccess *folderResourceAccessStub) (*dialect.Response, error) {
				return ReplaceFolderSharesNeutralHandler(context.Background(), &dialect.Request{
					Version: "v63.0",
					User:    folderTestUser(),
					Params:  map[string]string{"id": folderID},
					Body:    []byte(`{"shares":[{"sharedToId":"ALL_INTERNAL","sharedToType":"Organization","accessLevel":"view"}]}`),
				}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, policy))
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			resourceAccess := &folderResourceAccessStub{}
			resp, err := tc.call(resourceAccess)
			if err != nil {
				t.Fatalf("handler returned error: %v", err)
			}
			assertSFError(t, resp, http.StatusBadRequest, wire.SFCodeInvalidRequest)
			if resourceAccess.createCalls != 0 || resourceAccess.replaceCalls != 0 {
				t.Fatalf("resource access writes = create %d replace %d, want none", resourceAccess.createCalls, resourceAccess.replaceCalls)
			}
		})
	}
}

func TestReplaceFolderSharesUsesResourceAccessSetMutation(t *testing.T) {
	folderID := "00l000000000010AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Shared"),
	}}
	query := &folderQueryStub{results: map[string]*contract.QueryResult{
		constants.TableUser: {Records: []contract.Record{{
			constants.FieldID:        "005000000000002AAA",
			constants.FieldFirstName: "Ada",
			constants.FieldLastName:  "Lovelace",
			constants.FieldUsername:  "ada@example.invalid",
		}}},
		constants.TableUserRole: {Records: []contract.Record{{
			constants.FieldID:            "00E000000000003AAA",
			constants.FieldUserRole_Name: "Sales Ops",
		}}},
	}}
	resourceAccess := &folderResourceAccessStub{}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	resp, err := ReplaceFolderSharesNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID},
		Body:    []byte(`{"shares":[{"sharedToId":"005000000000002AAA","sharedToType":"User","accessLevel":"manage"},{"sharedToId":"00E000000000003AAA","sharedToType":"roleandsubordinates","accessLevel":"view"}]}`),
	}, folderDeps(records, query, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	if resourceAccess.replaceCalls != 1 {
		t.Fatalf("ReplaceResourceAccessGrants calls = %d, want 1", resourceAccess.replaceCalls)
	}
	if resourceAccess.replaceReq.ResourceType != folderResourceAccessType || resourceAccess.replaceReq.ResourceID != folderID {
		t.Fatalf("replace request = %#v", resourceAccess.replaceReq)
	}
	if got := resourceAccess.replaceReq.Grants[1]; got.PrincipalType != "RoleAndSubordinates" || got.AccessLevel != "View" {
		t.Fatalf("normalized second grant = %#v", got)
	}
	assertBody(t, "folder share replace", resp.Body, "", `{"shares":[{"shareId":"share-replaced-1","accessType":"Manage","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Ada Lovelace","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-replaced-1"},{"shareId":"share-replaced-2","accessType":"View","shareType":"RoleAndSubordinates","sharedWithId":"00E000000000003AAA","sharedWithLabel":"Sales Ops","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-replaced-2"}]}`)
}

func TestReplaceFolderSharesPreservesPublicOrganizationGrant(t *testing.T) {
	folderID := "00l000000000010AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Public Reports", "Report", "Public"),
	}}
	query := &folderQueryStub{results: map[string]*contract.QueryResult{
		constants.TableUser: {Records: []contract.Record{{
			constants.FieldID:        "005000000000002AAA",
			constants.FieldFirstName: "Ada",
			constants.FieldLastName:  "Lovelace",
			constants.FieldUsername:  "ada@example.invalid",
		}}},
	}}
	resourceAccess := &folderResourceAccessStub{}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	resp, err := ReplaceFolderSharesNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID},
		Body:    []byte(`{"shares":[{"sharedToId":"005000000000002AAA","sharedToType":"User","accessLevel":"manage"}]}`),
	}, folderDeps(records, query, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	if resourceAccess.replaceCalls != 1 {
		t.Fatalf("ReplaceResourceAccessGrants calls = %d, want 1", resourceAccess.replaceCalls)
	}
	if len(resourceAccess.replaceReq.Grants) != 2 {
		t.Fatalf("replace grants = %#v, want user grant plus derived Organization grant", resourceAccess.replaceReq.Grants)
	}
	if got := resourceAccess.replaceReq.Grants[1]; got.PrincipalID != "ALL_INTERNAL" || got.PrincipalType != "Organization" || got.AccessLevel != "View" {
		t.Fatalf("derived org grant = %#v, want Organization View ALL_INTERNAL", got)
	}
	assertBody(t, "folder share replace public", resp.Body, "", `{"shares":[{"shareId":"share-replaced-1","accessType":"Manage","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Ada Lovelace","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-replaced-1"},{"shareId":"share-replaced-2","accessType":"View","shareType":"Organization","sharedWithId":"ALL_INTERNAL","sharedWithLabel":"All Internal Users","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-replaced-2"}]}`)
}

func TestReplaceFolderSharesSortsPublicDerivedOrganizationGrantByShareID(t *testing.T) {
	folderID := "00l000000000010AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Public Reports", "Report", "Public"),
	}}
	query := &folderQueryStub{results: map[string]*contract.QueryResult{
		constants.TableUser: {Records: []contract.Record{{
			constants.FieldID:        "005000000000002AAA",
			constants.FieldFirstName: "Ada",
			constants.FieldLastName:  "Lovelace",
			constants.FieldUsername:  "ada@example.invalid",
		}}},
	}}
	resourceAccess := &folderResourceAccessStub{replaceIDs: []string{"share-z", "share-a"}}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	resp, err := ReplaceFolderSharesNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID},
		Body:    []byte(`{"shares":[{"sharedToId":"005000000000002AAA","sharedToType":"User","accessLevel":"manage"}]}`),
	}, folderDeps(records, query, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	assertBody(t, "folder share replace public sorted", resp.Body, "", `{"shares":[{"shareId":"share-a","accessType":"View","shareType":"Organization","sharedWithId":"ALL_INTERNAL","sharedWithLabel":"All Internal Users","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-a"},{"shareId":"share-z","accessType":"Manage","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Ada Lovelace","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-z"}]}`)
}

func TestUpdateFolderShareProjectsV63ShareResponse(t *testing.T) {
	folderID := "00l000000000010AAA"
	shareID := "0F9000000000001AAA"
	sharedToID := "005000000000002AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Shared"),
	}}
	query := &folderQueryStub{results: map[string]*contract.QueryResult{
		constants.TableUser: {Records: []contract.Record{{
			constants.FieldID:        sharedToID,
			constants.FieldFirstName: "Ada",
			constants.FieldLastName:  "Lovelace",
			constants.FieldUsername:  "ada@example.invalid",
		}}},
	}}
	resourceAccess := &folderResourceAccessStub{grants: []contract.ResourceAccessGrant{{
		ID:            shareID,
		ResourceType:  folderResourceAccessType,
		ResourceID:    folderID,
		PrincipalID:   sharedToID,
		PrincipalType: "User",
		AccessLevel:   "Manage",
	}}}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	resp, err := UpdateFolderShareNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": folderID, "shareId": shareID},
		Body:    []byte(`{"accessLevel":"view"}`),
	}, folderDeps(records, query, &folderAccessStub{allowManage: true}, resourceAccess, policy))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	assertBody(t, "folder share update", resp.Body, "", `{"shareId":"0F9000000000001AAA","accessType":"View","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Ada Lovelace","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/0F9000000000001AAA"}`)
}

func TestUpdateFolderShareRequiresAccessLevelBeforeMutation(t *testing.T) {
	resourceAccess := &folderResourceAccessStub{}

	resp, err := UpdateFolderShareNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Params:  map[string]string{"id": "00l000000000010AAA", "shareId": "0F9000000000001AAA"},
		Body:    []byte(`{}`),
	}, folderDeps(&folderRecordsStub{}, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, folderPolicyStub{}))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	assertSFError(t, resp, http.StatusBadRequest, wire.SFCodeInvalidRequest)
	if resourceAccess.getCalls != 0 || resourceAccess.updateCalls != 0 {
		t.Fatalf("resource access calls = get %d update %d, want none", resourceAccess.getCalls, resourceAccess.updateCalls)
	}
}

func TestTransferFolderOwnershipRequiresUserIDs(t *testing.T) {
	resourceAccess := &folderResourceAccessStub{}

	resp, err := TransferFolderOwnershipNeutralHandler(context.Background(), &dialect.Request{
		Version: "v63.0",
		User:    folderTestUser(),
		Body:    []byte(`{}`),
	}, folderDeps(&folderRecordsStub{}, &folderQueryStub{}, &folderAccessStub{}, resourceAccess, folderPolicyStub{string(constants.PermManageUsers): true}))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	assertSFError(t, resp, http.StatusBadRequest, wire.SFCodeRequiredFieldMissing)
	if resourceAccess.transferCalls != 0 {
		t.Fatalf("transfer calls = %d, want none", resourceAccess.transferCalls)
	}
}

func TestOrganizationFolderShareCannotBePatchedOrDeleted(t *testing.T) {
	folderID := "00l000000000010AAA"
	shareID := "0F9000000000001AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Public Reports", "Report", "Public"),
	}}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	tests := []struct {
		name string
		call func(*folderResourceAccessStub) (*dialect.Response, error)
	}{
		{
			name: "patch organization",
			call: func(resourceAccess *folderResourceAccessStub) (*dialect.Response, error) {
				return UpdateFolderShareNeutralHandler(context.Background(), &dialect.Request{
					Version: "v63.0",
					User:    folderTestUser(),
					Params:  map[string]string{"id": folderID, "shareId": shareID},
					Body:    []byte(`{"accessLevel":"manage"}`),
				}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, policy))
			},
		},
		{
			name: "delete organization",
			call: func(resourceAccess *folderResourceAccessStub) (*dialect.Response, error) {
				return DeleteFolderShareNeutralHandler(context.Background(), &dialect.Request{
					Version: "v63.0",
					User:    folderTestUser(),
					Params:  map[string]string{"id": folderID, "shareId": shareID},
				}, folderDeps(records, &folderQueryStub{}, &folderAccessStub{allowManage: true}, resourceAccess, policy))
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			resourceAccess := &folderResourceAccessStub{grants: []contract.ResourceAccessGrant{{
				ID:            shareID,
				ResourceType:  folderResourceAccessType,
				ResourceID:    folderID,
				PrincipalID:   "ALL_INTERNAL",
				PrincipalType: "Organization",
				AccessLevel:   "View",
			}}}
			resp, err := tc.call(resourceAccess)
			if err != nil {
				t.Fatalf("handler returned error: %v", err)
			}
			assertSFError(t, resp, http.StatusBadRequest, wire.SFCodeInvalidRequest)
			if resourceAccess.updateCalls != 0 || resourceAccess.deleteCalls != 0 {
				t.Fatalf("resource access writes = update %d delete %d, want none", resourceAccess.updateCalls, resourceAccess.deleteCalls)
			}
		})
	}
}

func TestFolderShareMutationsIgnorePostCommitLabelLookupFailure(t *testing.T) {
	folderID := "00l000000000010AAA"
	shareID := "0F9000000000001AAA"
	sharedToID := "005000000000002AAA"
	records := &folderRecordsStub{retrieve: map[string]contract.Record{
		constants.TableReportFolder + ":" + folderID: folderRecord(folderID, "Shared Reports", "Report", "Shared"),
	}}
	policy := folderPolicyStub{string(constants.PermManageReportsInPubFolders): true}

	tests := []struct {
		name       string
		resource   *folderResourceAccessStub
		call       func(dialect.Deps) (*dialect.Response, error)
		wantStatus int
		wantBody   string
	}{
		{
			name:     "add",
			resource: &folderResourceAccessStub{},
			call: func(deps dialect.Deps) (*dialect.Response, error) {
				return AddFolderSharesNeutralHandler(context.Background(), &dialect.Request{
					Version: "v63.0",
					User:    folderTestUser(),
					Params:  map[string]string{"id": folderID},
					Body:    []byte(`{"shares":[{"sharedToId":"005000000000002AAA","sharedToType":"User","accessLevel":"manage"}]}`),
				}, deps)
			},
			wantStatus: http.StatusCreated,
			wantBody:   `{"shares":[{"shareId":"share-created-1","accessType":"Manage","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Unknown User","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-created-1"}]}`,
		},
		{
			name:     "replace",
			resource: &folderResourceAccessStub{},
			call: func(deps dialect.Deps) (*dialect.Response, error) {
				return ReplaceFolderSharesNeutralHandler(context.Background(), &dialect.Request{
					Version: "v63.0",
					User:    folderTestUser(),
					Params:  map[string]string{"id": folderID},
					Body:    []byte(`{"shares":[{"sharedToId":"00E000000000003AAA","sharedToType":"role","accessLevel":"view"}]}`),
				}, deps)
			},
			wantStatus: http.StatusOK,
			wantBody:   `{"shares":[{"shareId":"share-replaced-1","accessType":"View","shareType":"Role","sharedWithId":"00E000000000003AAA","sharedWithLabel":"Unknown Role","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/share-replaced-1"}]}`,
		},
		{
			name: "update",
			resource: &folderResourceAccessStub{grants: []contract.ResourceAccessGrant{{
				ID:            shareID,
				ResourceType:  folderResourceAccessType,
				ResourceID:    folderID,
				PrincipalID:   sharedToID,
				PrincipalType: "User",
				AccessLevel:   "Manage",
			}}},
			call: func(deps dialect.Deps) (*dialect.Response, error) {
				return UpdateFolderShareNeutralHandler(context.Background(), &dialect.Request{
					Version: "v63.0",
					User:    folderTestUser(),
					Params:  map[string]string{"id": folderID, "shareId": shareID},
					Body:    []byte(`{"accessLevel":"view"}`),
				}, deps)
			},
			wantStatus: http.StatusOK,
			wantBody:   `{"shareId":"0F9000000000001AAA","accessType":"View","shareType":"User","sharedWithId":"005000000000002AAA","sharedWithLabel":"Unknown User","url":"/services/data/v63.0/folders/00l000000000010AAA/shares/0F9000000000001AAA"}`,
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			logs := captureFolderLogs(t)
			deps := folderDeps(records, &folderQueryStub{err: errors.New("label lookup down")}, &folderAccessStub{allowManage: true}, tc.resource, policy)
			resp, err := tc.call(deps)
			if err != nil {
				t.Fatalf("handler returned error: %v", err)
			}
			if resp.Status != tc.wantStatus {
				t.Fatalf("status = %d, want %d; body=%#v", resp.Status, tc.wantStatus, resp.Body)
			}
			assertBody(t, "folder share mutation label fallback "+tc.name, resp.Body, "", tc.wantBody)
			if got := logs.String(); !strings.Contains(got, "label lookup down") {
				t.Fatalf("logs = %q, want label lookup failure", got)
			}
		})
	}
}

func folderDeps(records contract.Records, query contract.Query, access contract.FolderAccess, resourceAccess contract.ResourceAccess, policy contract.Policy) dialect.Deps {
	return dialect.Deps{Contracts: contract.Deps{
		Records:        records,
		Query:          query,
		FolderAccess:   access,
		ResourceAccess: resourceAccess,
		Policy:         policy,
	}}
}

func folderTestUser() *models.UserSession {
	return &models.UserSession{ID: "005000000000001AAA"}
}

func folderRecord(id, name, folderType, accessType string) contract.Record {
	return contract.Record{
		constants.FieldReportFolder_ID:            id,
		constants.FieldReportFolder_Name:          name,
		constants.FieldReportFolder_DeveloperName: strings.ReplaceAll(name, " ", "_"),
		constants.FieldReportFolder_Type:          folderType,
		constants.FieldReportFolder_AccessType:    accessType,
		constants.FieldReportFolder_OwnerID:       "005000000000001AAA",
		constants.FieldReportFolder_CreatedByID:   "005000000000001AAA",
	}
}

func captureFolderLogs(t *testing.T) *bytes.Buffer {
	t.Helper()
	var output bytes.Buffer
	oldWriter := log.Writer()
	oldFlags := log.Flags()
	log.SetOutput(&output)
	log.SetFlags(0)
	t.Cleanup(func() {
		log.SetOutput(oldWriter)
		log.SetFlags(oldFlags)
	})
	return &output
}

type folderRecordsStub struct {
	retrieve    map[string]contract.Record
	retrieveErr error
	insertFn    func(object string, data contract.Record) (contract.Record, error)
	inserted    []contract.Record
}

func (f *folderRecordsStub) Insert(_ context.Context, object string, data contract.Record, _ *models.UserSession) (contract.Record, error) {
	f.inserted = append(f.inserted, data)
	if f.insertFn != nil {
		return f.insertFn(object, data)
	}
	return data, nil
}

func (f *folderRecordsStub) Retrieve(_ context.Context, object string, id string, _ *models.UserSession) (contract.Record, error) {
	if f.retrieveErr != nil {
		return nil, f.retrieveErr
	}
	if f.retrieve != nil {
		if record, ok := f.retrieve[object+":"+id]; ok {
			return record, nil
		}
	}
	return nil, errors.New("not found")
}

func (f *folderRecordsStub) RetrieveMany(context.Context, string, []string, *models.UserSession) (map[string]contract.Record, error) {
	return nil, nil
}
func (f *folderRecordsStub) Update(context.Context, string, string, contract.Record, *models.UserSession) error {
	return nil
}
func (f *folderRecordsStub) Delete(context.Context, string, string, *models.UserSession) error {
	return nil
}
func (f *folderRecordsStub) UpsertByExternalID(context.Context, string, string, string, contract.Record, *models.UserSession) (contract.Record, bool, error) {
	return nil, false, nil
}
func (f *folderRecordsStub) RetrieveByField(context.Context, string, string, string, *models.UserSession) (contract.Record, error) {
	return nil, nil
}
func (f *folderRecordsStub) CreateRecords(context.Context, []contract.Record, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}
func (f *folderRecordsStub) UpdateRecords(context.Context, []contract.Record, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}
func (f *folderRecordsStub) DeleteRecords(context.Context, []string, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}
func (f *folderRecordsStub) UpsertRecords(context.Context, string, string, []contract.Record, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}
func (f *folderRecordsStub) RetrieveRecords(context.Context, string, []string, []string, *models.UserSession) ([]contract.Record, error) {
	return nil, nil
}
func (f *folderRecordsStub) ObjectTypeForID(context.Context, string) (string, error) {
	return "", nil
}

type folderQueryStub struct {
	result  *contract.QueryResult
	results map[string]*contract.QueryResult
	queryFn func(queryspec.QuerySpec) (*contract.QueryResult, error)
	err     error
	reqs    []queryspec.QuerySpec
}

func (f *folderQueryStub) Query(_ context.Context, req queryspec.QuerySpec, _ *models.UserSession) (*contract.QueryResult, error) {
	f.reqs = append(f.reqs, req)
	if f.queryFn != nil {
		return f.queryFn(req)
	}
	if f.results != nil {
		if result, ok := f.results[req.ObjectAPIName]; ok {
			return result, f.err
		}
	}
	if f.result != nil {
		return f.result, f.err
	}
	return &contract.QueryResult{}, f.err
}

func (f *folderQueryStub) SearchWithSpec(context.Context, queryspec.SearchSpec, *models.UserSession) ([]contract.Record, error) {
	return nil, nil
}
func (f *folderQueryStub) GlobalSearchWithSpec(context.Context, queryspec.GlobalSearchSpec, *models.UserSession) ([]contract.SearchResult, error) {
	return nil, nil
}
func (f *folderQueryStub) QueryHistory(context.Context, contract.HistoryQueryRequest, *models.UserSession) (*contract.HistoryQueryResult, error) {
	return nil, nil
}

type folderAccessStub struct {
	allowView   bool
	allowManage bool
}

func (f *folderAccessStub) CheckFolderAccess(_ context.Context, _ *models.UserSession, _ string, required contract.FolderAccessLevel) bool {
	if required == contract.FolderAccessManage {
		return f.allowManage
	}
	return f.allowView || f.allowManage
}

type folderResourceAccessStub struct {
	grants            []contract.ResourceAccessGrant
	visibleIDs        []string
	visibleErr        error
	listCalls         int
	listReq           contract.ResourceAccessListRequest
	listErr           error
	getCalls          int
	getReq            contract.ResourceAccessGetRequest
	getErr            error
	createCalls       int
	createReqs        []contract.ResourceAccessCreateRequest
	createIDs         []string
	ensureCalls       int
	ensureReq         contract.ResourceAccessEnsureRequest
	ensureErr         error
	replaceCalls      int
	replaceReq        contract.ResourceAccessReplaceRequest
	replaceIDs        []string
	updateCalls       int
	updateReq         contract.ResourceAccessUpdateRequest
	deleteCalls       int
	deleteReq         contract.ResourceAccessDeleteRequest
	deleteByTypeCalls int
	deleteByTypeReq   contract.ResourceAccessDeleteByPrincipalTypeRequest
	deleteByTypeErr   error
	transferCalls     int
	transferReq       contract.ResourceOwnershipTransferRequest
	nextID            int
}

func (f *folderResourceAccessStub) ListVisibleResourceIDs(_ context.Context, _ *models.UserSession, _ contract.ResourceVisibilityRequest) ([]string, error) {
	return f.visibleIDs, f.visibleErr
}

func (f *folderResourceAccessStub) ListResourceAccessGrants(_ context.Context, _ *models.UserSession, req contract.ResourceAccessListRequest) ([]contract.ResourceAccessGrant, error) {
	f.listCalls++
	f.listReq = req
	return f.grants, f.listErr
}

func (f *folderResourceAccessStub) GetResourceAccessGrant(_ context.Context, _ *models.UserSession, req contract.ResourceAccessGetRequest) (*contract.ResourceAccessGrant, error) {
	f.getCalls++
	f.getReq = req
	if f.getErr != nil {
		return nil, f.getErr
	}
	for i := range f.grants {
		if f.grants[i].ID == req.GrantID && f.grants[i].ResourceType == req.ResourceType {
			return &f.grants[i], nil
		}
	}
	return nil, nil
}

func (f *folderResourceAccessStub) CreateResourceAccessGrant(_ context.Context, _ *models.UserSession, req contract.ResourceAccessCreateRequest) (*contract.ResourceAccessGrant, error) {
	f.createCalls++
	f.createReqs = append(f.createReqs, req)
	f.nextID++
	id := "share-created-" + strconv.Itoa(f.nextID)
	if f.nextID <= len(f.createIDs) {
		id = f.createIDs[f.nextID-1]
	}
	grant := contract.ResourceAccessGrant{
		ID:            id,
		ResourceType:  req.ResourceType,
		ResourceID:    req.ResourceID,
		PrincipalID:   req.Grant.PrincipalID,
		PrincipalType: req.Grant.PrincipalType,
		AccessLevel:   req.Grant.AccessLevel,
	}
	f.grants = append(f.grants, grant)
	return &grant, nil
}

func (f *folderResourceAccessStub) EnsureResourceAccessGrant(ctx context.Context, user *models.UserSession, req contract.ResourceAccessEnsureRequest) (*contract.ResourceAccessGrant, error) {
	f.ensureCalls++
	f.ensureReq = req
	if f.ensureErr != nil {
		return nil, f.ensureErr
	}
	for i := range f.grants {
		if f.grants[i].ResourceID == req.ResourceID && f.grants[i].PrincipalType == req.Grant.PrincipalType {
			return &f.grants[i], nil
		}
	}
	return f.CreateResourceAccessGrant(ctx, user, contract.ResourceAccessCreateRequest{
		ResourceType: req.ResourceType,
		ResourceID:   req.ResourceID,
		CreatedByID:  req.CreatedByID,
		Grant:        req.Grant,
	})
}

func (f *folderResourceAccessStub) ReplaceResourceAccessGrants(_ context.Context, _ *models.UserSession, req contract.ResourceAccessReplaceRequest) ([]contract.ResourceAccessGrant, error) {
	f.replaceCalls++
	f.replaceReq = req
	grants := make([]contract.ResourceAccessGrant, 0, len(req.Grants))
	for i, input := range req.Grants {
		id := "share-replaced-" + strconv.Itoa(i+1)
		if i < len(f.replaceIDs) {
			id = f.replaceIDs[i]
		}
		grants = append(grants, contract.ResourceAccessGrant{
			ID:            id,
			ResourceType:  req.ResourceType,
			ResourceID:    req.ResourceID,
			PrincipalID:   input.PrincipalID,
			PrincipalType: input.PrincipalType,
			AccessLevel:   input.AccessLevel,
		})
	}
	f.grants = grants
	return grants, nil
}

func (f *folderResourceAccessStub) UpdateResourceAccessGrant(_ context.Context, _ *models.UserSession, req contract.ResourceAccessUpdateRequest) (*contract.ResourceAccessGrant, error) {
	f.updateCalls++
	f.updateReq = req
	for i := range f.grants {
		if f.grants[i].ID == req.GrantID {
			f.grants[i].AccessLevel = req.AccessLevel
			return &f.grants[i], nil
		}
	}
	return nil, errors.New("not found")
}

func (f *folderResourceAccessStub) DeleteResourceAccessGrant(_ context.Context, _ *models.UserSession, req contract.ResourceAccessDeleteRequest) error {
	f.deleteCalls++
	f.deleteReq = req
	return nil
}

func (f *folderResourceAccessStub) DeleteResourceAccessGrantsByPrincipalType(_ context.Context, _ *models.UserSession, req contract.ResourceAccessDeleteByPrincipalTypeRequest) error {
	f.deleteByTypeCalls++
	f.deleteByTypeReq = req
	return f.deleteByTypeErr
}

func (f *folderResourceAccessStub) TransferResourceOwnership(_ context.Context, _ *models.UserSession, req contract.ResourceOwnershipTransferRequest) error {
	f.transferCalls++
	f.transferReq = req
	return nil
}

type folderPolicyStub map[string]bool

func (f folderPolicyStub) Access(context.Context, *models.UserSession, string, contract.AccessRequest) (*contract.EffectiveObjectAccess, error) {
	return nil, nil
}

func (f folderPolicyStub) HasCapability(_ context.Context, _ *models.UserSession, capability contract.Capability) bool {
	return f[string(capability)]
}

type folderFieldWriteDeniedError struct{}

func (folderFieldWriteDeniedError) Error() string {
	return "fields denied"
}

func (folderFieldWriteDeniedError) FieldWriteDeniedFields() []string {
	return []string{"Name", "DeveloperName"}
}

func (folderFieldWriteDeniedError) FieldWriteDeniedMessage() string {
	return "fields denied"
}

type folderValidationRuleError struct{}

func (folderValidationRuleError) Error() string {
	return "validation rule: Folder rule failed"
}

func (folderValidationRuleError) ValidationRuleMessage() string {
	return "Folder rule failed"
}
