package files

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

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

type captureFileRecordStore struct {
	inserted models.SObject
}

func (s *captureFileRecordStore) Insert(_ context.Context, objectName string, data models.SObject, _ *models.UserSession) (models.SObject, error) {
	if objectName != constants.TableFile {
		return nil, fmt.Errorf("Insert objectName = %q; want %q", objectName, constants.TableFile)
	}
	s.inserted = cloneFileRecord(data)
	return data, nil
}

func (s *captureFileRecordStore) Retrieve(_ context.Context, objectName string, id string, _ *models.UserSession) (models.SObject, error) {
	if objectName != "Contract" || id != "800000000000001" {
		return nil, fmt.Errorf("unexpected parent retrieve %s/%s", objectName, id)
	}
	return models.SObject{constants.FieldID: id}, nil
}

func (s *captureFileRecordStore) GetRaw(context.Context, string, string) (models.SObject, error) {
	if s.inserted != nil {
		return cloneFileRecord(s.inserted), nil
	}
	return nil, nil
}

func (s *captureFileRecordStore) Update(context.Context, string, string, models.SObject, *models.UserSession) error {
	return nil
}

type sourceFileRecordStore struct {
	files     map[string]models.SObject
	parents   map[string]models.SObject
	retrieved []string
}

func (s *sourceFileRecordStore) Insert(context.Context, string, models.SObject, *models.UserSession) (models.SObject, error) {
	return nil, nil
}

func (s *sourceFileRecordStore) Retrieve(_ context.Context, objectName string, id string, _ *models.UserSession) (models.SObject, error) {
	s.retrieved = append(s.retrieved, objectName+"/"+id)
	parent := s.parents[objectName+"/"+id]
	if parent == nil {
		return nil, fmt.Errorf("missing parent %s/%s", objectName, id)
	}
	return parent, nil
}

func (s *sourceFileRecordStore) GetRaw(_ context.Context, objectName string, id string) (models.SObject, error) {
	if objectName != constants.TableFile {
		return nil, nil
	}
	return s.files[id], nil
}

func (s *sourceFileRecordStore) Update(context.Context, string, string, models.SObject, *models.UserSession) error {
	return nil
}

type allowFilePermissionChecker struct{}

func (allowFilePermissionChecker) CheckPermissionOrErrorWithUser(context.Context, string, string, *models.UserSession) error {
	return nil
}

type denyFileReadPermissionChecker struct{}

func (denyFileReadPermissionChecker) CheckPermissionOrErrorWithUser(_ context.Context, objectName string, operation string, _ *models.UserSession) error {
	if objectName == constants.TableFile && operation == constants.PermRead {
		return fmt.Errorf("file read denied")
	}
	return nil
}

type recordAttachmentSourceLookupSpy struct {
	result            *contract.RecordAttachmentSourceLookupResult
	found             bool
	err               error
	calls             []contract.RecordAttachmentSourceRequest
	parentReadsAtCall []int
	parentReads       func() int
}

func (s *recordAttachmentSourceLookupSpy) LookupRecordAttachmentSource(_ context.Context, req contract.RecordAttachmentSourceRequest) (*contract.RecordAttachmentSourceLookupResult, bool, error) {
	s.calls = append(s.calls, req)
	if s.parentReads != nil {
		s.parentReadsAtCall = append(s.parentReadsAtCall, s.parentReads())
	}
	return s.result, s.found, s.err
}

type memoryObjectStore struct{}

func (memoryObjectStore) PutObject(_ context.Context, key string, body io.Reader, opts storage.PutObjectOptions) (storage.ObjectMetadata, error) {
	size, err := io.Copy(io.Discard, body)
	if err != nil {
		return storage.ObjectMetadata{}, err
	}
	contentType := opts.ContentType
	if contentType == "" {
		contentType = "application/octet-stream"
	}
	return storage.ObjectMetadata{
		Key:          key,
		Size:         size,
		ContentType:  contentType,
		LastModified: time.Unix(1700000000, 0),
	}, nil
}

func (memoryObjectStore) GetObject(context.Context, string) (io.ReadCloser, storage.ObjectMetadata, error) {
	return nil, storage.ObjectMetadata{}, storage.ErrObjectNotFound
}

func (memoryObjectStore) HeadObject(context.Context, string) (storage.ObjectMetadata, error) {
	return storage.ObjectMetadata{}, storage.ErrObjectNotFound
}

func (memoryObjectStore) DeleteObject(context.Context, string) error {
	return nil
}

func (memoryObjectStore) GeneratePresignedGetURL(context.Context, string, time.Duration) (string, error) {
	return "", storage.ErrPresignUnsupported
}

type keyedObjectStore struct {
	objects map[string]string
	getKeys []string
}

func (s *keyedObjectStore) PutObject(context.Context, string, io.Reader, storage.PutObjectOptions) (storage.ObjectMetadata, error) {
	return storage.ObjectMetadata{}, nil
}

func (s *keyedObjectStore) GetObject(_ context.Context, key string) (io.ReadCloser, storage.ObjectMetadata, error) {
	s.getKeys = append(s.getKeys, key)
	body, ok := s.objects[key]
	if !ok {
		return nil, storage.ObjectMetadata{}, storage.ErrObjectNotFound
	}
	return io.NopCloser(strings.NewReader(body)), storage.ObjectMetadata{
		Key:         key,
		Size:        int64(len(body)),
		ContentType: "text/plain",
	}, nil
}

func (*keyedObjectStore) HeadObject(context.Context, string) (storage.ObjectMetadata, error) {
	return storage.ObjectMetadata{}, storage.ErrObjectNotFound
}

func (*keyedObjectStore) DeleteObject(context.Context, string) error {
	return nil
}

func (*keyedObjectStore) GeneratePresignedGetURL(context.Context, string, time.Duration) (string, error) {
	return "", storage.ErrPresignUnsupported
}

func TestStoreRecordAttachmentDefaultsFilesystemStorageBucket(t *testing.T) {
	records := &captureFileRecordStore{}
	service := NewContractService(
		memoryObjectStore{},
		storage.ProviderFilesystem,
		"",
		"",
		records,
		allowFilePermissionChecker{},
		nil,
	)

	_, err := service.StoreRecordAttachment(context.Background(), contract.FileUploadRequest{
		FileName:            "Archived Contract.pdf",
		Extension:           ".pdf",
		ContentType:         "application/pdf",
		Body:                strings.NewReader("%PDF-1.4"),
		ContentLength:       int64(len("%PDF-1.4")),
		ParentID:            "800000000000001",
		ParentObjectAPIName: "Contract",
		AttachmentKind:      defaultRecordAttachmentKind,
		User: &models.UserSession{
			ID:        "005000000000001",
			Name:      "Admin User",
			ProfileID: constants.ProfileSystemAdmin,
		},
	})
	if err != nil {
		t.Fatalf("StoreRecordAttachment err = %v", err)
	}

	if got := records.inserted[constants.FieldSysFile_StorageProvider]; got != storage.ProviderFilesystem {
		t.Fatalf("StorageProvider = %v; want %q", got, storage.ProviderFilesystem)
	}
	if got := records.inserted[constants.FieldSysFile_StorageBucket]; got != storage.DefaultFilesystemBucket {
		t.Fatalf("StorageBucket = %v; want %q", got, storage.DefaultFilesystemBucket)
	}
	if got := records.inserted[constants.FieldSysFile_StorageKey]; got == "" {
		t.Fatal("StorageKey is empty")
	}
	if got, _ := records.inserted[constants.FieldSysFile_Checksum].(string); !strings.HasPrefix(got, "sha256:") {
		t.Fatalf("Checksum = %q, want sha256 digest", got)
	}
}

func TestStoreRecordAttachmentReplayConsumesAndBindsContent(t *testing.T) {
	records := &captureFileRecordStore{}
	service := NewContractService(
		memoryObjectStore{},
		storage.ProviderFilesystem,
		"",
		"",
		records,
		allowFilePermissionChecker{},
		nil,
	)
	request := contract.FileUploadRequest{
		ID:                  "file-idempotency-id",
		FileName:            "contract.txt",
		ContentType:         "text/plain",
		ContentLength:       int64(len("same")),
		ParentID:            "800000000000001",
		ParentObjectAPIName: "Contract",
		User:                &models.UserSession{ID: "005000000000001"},
	}
	request.Body = strings.NewReader("same")
	first, err := service.StoreRecordAttachment(context.Background(), request)
	if err != nil {
		t.Fatal(err)
	}
	replayBody := strings.NewReader("same")
	request.Body = replayBody
	second, err := service.StoreRecordAttachment(context.Background(), request)
	if err != nil {
		t.Fatal(err)
	}
	if first.ID != second.ID || replayBody.Len() != 0 {
		t.Fatalf("replay result = %#v, remaining body = %d", second, replayBody.Len())
	}
	request.Body = strings.NewReader("diff")
	if _, err := service.StoreRecordAttachment(context.Background(), request); err == nil {
		t.Fatal("same idempotency identity accepted different content")
	}
}

func TestOpenDetachedFileUsesInjectedBlobReference(t *testing.T) {
	store := &keyedObjectStore{objects: map[string]string{"pack-owned/blob-key": "raw"}}
	service := NewContractService(
		store,
		storage.ProviderFilesystem,
		"",
		"",
		nil,
		allowFilePermissionChecker{},
		nil,
		WithDetachedReferenceResolver(systemFileSourceTypeContentCompat, func(reference string) (contract.FileBlobReference, bool) {
			if reference != "legacy-wire-reference" {
				return contract.FileBlobReference{}, false
			}
			return contract.FileBlobReference{
				StorageKey:     "pack-owned/blob-key",
				LegacyFileName: "legacy-name.txt",
				FileName:       "download.txt",
			}, true
		}),
	)

	ref := service.resolveDetachedReference(systemFileSourceTypeContentCompat, "legacy-wire-reference")
	got, err := service.OpenDetachedFile(context.Background(), ref, &models.UserSession{ID: "005000000000001"})
	if err != nil {
		t.Fatalf("OpenDetachedFile err = %v", err)
	}
	defer got.Reader.Close()
	if got.FileName != "download.txt" {
		t.Fatalf("FileName = %q, want download.txt", got.FileName)
	}
	if len(store.getKeys) != 1 || store.getKeys[0] != "pack-owned/blob-key" {
		t.Fatalf("GetObject keys = %#v, want pack-owned/blob-key", store.getKeys)
	}
}

func TestOpenStoredObjectUsesSourceScopedStorageAuthority(t *testing.T) {
	primary := storage.Config{
		Provider: storage.ProviderS3, Bucket: "customer-files", Region: "us-west-2",
		KeyPrefix: "customer/v1", Endpoint: "https://s3.example.invalid",
	}
	catalog, err := storage.NewAuthorityCatalog(primary, `[{"name":"history","source_type":"archive","provider":"s3","bucket":"history-files","region":"us-east-1","key_prefix":"history/v2"}]`)
	if err != nil {
		t.Fatal(err)
	}
	historyStore := &keyedObjectStore{objects: map[string]string{"068-file": "history"}}
	service := NewContractService(
		&keyedObjectStore{}, storage.ProviderS3, "customer-files", "", nil,
		allowFilePermissionChecker{}, nil, WithStorageAuthorityCatalog(catalog),
	)
	service.resolveStore = func(config storage.Config) (storage.ObjectStore, error) {
		if config.Provider != storage.ProviderS3 || config.Bucket != "history-files" ||
			config.Region != "us-east-1" || config.KeyPrefix != "history/v2" ||
			config.Endpoint != primary.Endpoint {
			t.Fatalf("resolved config = %#v", config)
		}
		return historyStore, nil
	}

	reader, _, err := service.openStoredObject(t.Context(), models.SObject{
		constants.FieldSysFile_StorageProvider: storage.ProviderS3,
		constants.FieldSysFile_StorageBucket:   "history-files",
		constants.FieldSysFile_SourceType:      "ARCHIVE",
	}, "068-file")
	if err != nil {
		t.Fatal(err)
	}
	defer reader.Close()
	if len(historyStore.getKeys) != 1 || historyStore.getKeys[0] != "068-file" {
		t.Fatalf("history store keys = %#v", historyStore.getKeys)
	}
}

func TestOpenStoredObjectRejectsWrongSourceForGovernedAuthority(t *testing.T) {
	primary := storage.Config{Provider: storage.ProviderS3, Bucket: "customer-files", Region: "us-west-2", KeyPrefix: "customer/v1"}
	catalog, err := storage.NewAuthorityCatalog(primary, `[{"name":"history","source_type":"archive","provider":"s3","bucket":"history-files","region":"us-east-1","key_prefix":"history/v2"}]`)
	if err != nil {
		t.Fatal(err)
	}
	service := NewContractService(
		&keyedObjectStore{}, storage.ProviderS3, "customer-files", "", nil,
		allowFilePermissionChecker{}, nil, WithStorageAuthorityCatalog(catalog),
	)
	service.resolveStore = func(storage.Config) (storage.ObjectStore, error) {
		t.Fatal("wrong source type reached the governed storage authority")
		return nil, nil
	}

	_, _, err = service.openStoredObject(t.Context(), models.SObject{
		constants.FieldSysFile_StorageProvider: storage.ProviderS3,
		constants.FieldSysFile_StorageBucket:   "history-files",
		constants.FieldSysFile_SourceType:      "native",
	}, "068-file")
	if err == nil || !strings.Contains(err.Error(), "not admitted") {
		t.Fatalf("error = %v, want source-type authority rejection", err)
	}
}

func TestWithoutLegacyFileFallbackFailsClosedWhenObjectStorageMisses(t *testing.T) {
	legacyRoot := t.TempDir()
	if err := os.WriteFile(filepath.Join(legacyRoot, "legacy.txt"), []byte("must-not-open"), 0o600); err != nil {
		t.Fatal(err)
	}
	service := NewContractService(
		&keyedObjectStore{},
		storage.ProviderFilesystem,
		"",
		legacyRoot,
		nil,
		allowFilePermissionChecker{},
		nil,
		WithoutLegacyFileFallback(),
	)

	_, err := service.OpenDetachedFile(
		context.Background(),
		contract.FileBlobReference{StorageKey: "missing/object", LegacyFileName: "legacy.txt"},
		&models.UserSession{ID: "005000000000001"},
	)
	if err == nil {
		t.Fatal("OpenDetachedFile used the legacy filesystem fallback")
	}
}

func TestDetachedReferenceResolverIsSourceTypeScoped(t *testing.T) {
	service := NewContractService(
		&keyedObjectStore{},
		storage.ProviderFilesystem,
		"",
		"",
		nil,
		allowFilePermissionChecker{},
		nil,
		WithDetachedReferenceResolver(systemFileSourceTypeContentCompat, func(reference string) (contract.FileBlobReference, bool) {
			return contract.FileBlobReference{StorageKey: "resolved/" + reference}, true
		}),
	)

	ref := service.resolveDetachedReference(systemFileSourceTypeHrefCompat, "legacy-wire-reference")
	if ref.StorageKey != "legacy-wire-reference" {
		t.Fatalf("StorageKey = %q, want fallback storage path", ref.StorageKey)
	}

	ref = service.resolveDetachedReference(systemFileSourceTypeContentCompat, "legacy-wire-reference")
	if ref.StorageKey != "resolved/legacy-wire-reference" {
		t.Fatalf("StorageKey = %q, want source-type resolver output", ref.StorageKey)
	}
}

func TestAuthorizeRecordAttachmentMetadataAuthorizesWithoutOpeningBlob(t *testing.T) {
	store := &keyedObjectStore{objects: map[string]string{"files/file-1": "authorized"}}
	records := &sourceFileRecordStore{
		files: map[string]models.SObject{
			"file-1": authorizedSystemFileRecord("parent-1", "files/file-1", "Authorized.pdf"),
		},
		parents: map[string]models.SObject{
			"Contract/parent-1": {constants.FieldID: "parent-1"},
		},
	}
	service := NewContractService(store, storage.ProviderFilesystem, "", "", records, allowFilePermissionChecker{}, nil)

	result, found, err := service.AuthorizeRecordAttachmentMetadata(context.Background(), "file-1", &models.UserSession{ID: "user-1"})
	if err != nil {
		t.Fatalf("AuthorizeRecordAttachmentMetadata err = %v", err)
	}
	if !found {
		t.Fatal("AuthorizeRecordAttachmentMetadata found = false, want true")
	}
	if result == nil || result.FileID != "file-1" || result.FileName != "Authorized.pdf" {
		t.Fatalf("AuthorizeRecordAttachmentMetadata result = %#v", result)
	}
	if len(records.retrieved) != 1 || records.retrieved[0] != "Contract/parent-1" {
		t.Fatalf("parent retrieves = %#v, want Contract/parent-1", records.retrieved)
	}
	if len(store.getKeys) != 0 {
		t.Fatalf("storage gets = %#v, want none for metadata-only authorization", store.getKeys)
	}
}

func TestRecordAttachmentMetadataAuthorizerRequiresNoBlobStore(t *testing.T) {
	records := &sourceFileRecordStore{
		files: map[string]models.SObject{
			"file-1": authorizedSystemFileRecord("parent-1", "files/file-1", "Metadata-only.pdf"),
		},
		parents: map[string]models.SObject{
			"Contract/parent-1": {constants.FieldID: "parent-1"},
		},
	}
	authorizer, err := NewRecordAttachmentMetadataAuthorizer(
		records,
		allowFilePermissionChecker{},
		nil,
	)
	if err != nil {
		t.Fatal(err)
	}

	result, found, err := authorizer.AuthorizeRecordAttachmentMetadata(
		context.Background(),
		"file-1",
		&models.UserSession{ID: "user-1"},
	)
	if err != nil || !found || result == nil || result.FileName != "Metadata-only.pdf" {
		t.Fatalf("metadata-only authorization = result %#v, found %v, err %v", result, found, err)
	}
}

func TestAuthorizeRecordAttachmentMetadataRejectsLegacyNullableDeliveryFacts(t *testing.T) {
	record := authorizedSystemFileRecord("parent-1", "files/file-1", "Legacy.pdf")
	delete(record, constants.FieldSysFile_MimeType)
	delete(record, constants.FieldSysFile_Checksum)
	service := NewContractService(
		&keyedObjectStore{}, storage.ProviderFilesystem, "", "",
		&sourceFileRecordStore{
			files:   map[string]models.SObject{"file-1": record},
			parents: map[string]models.SObject{"Contract/parent-1": {constants.FieldID: "parent-1"}},
		},
		allowFilePermissionChecker{}, nil,
	)

	result, found, err := service.AuthorizeRecordAttachmentMetadata(
		context.Background(), "file-1", &models.UserSession{ID: "user-1"},
	)
	if err == nil || !found || result != nil {
		t.Fatalf("nullable delivery metadata = result %#v, found %v, err %v; want handled integrity failure", result, found, err)
	}
}

func TestOpenFrozenRecordAttachmentRejectsMetadataDrift(t *testing.T) {
	content := "frozen"
	sum := sha256.Sum256([]byte(content))
	record := authorizedSystemFileRecord("parent-1", "files/file-1", "資料.pdf")
	record[constants.FieldSysFile_SizeBytes] = int64(len(content))
	record[constants.FieldSysFile_MimeType] = "application/pdf"
	record[constants.FieldSysFile_Checksum] = "sha256:" + hex.EncodeToString(sum[:])
	service := NewContractService(
		&keyedObjectStore{objects: map[string]string{"files/file-1": content}},
		storage.ProviderFilesystem, "", "",
		&sourceFileRecordStore{files: map[string]models.SObject{"file-1": record}},
		allowFilePermissionChecker{}, nil,
	)
	expected := contract.RecordAttachmentMetadata{
		FileID: "file-1", FileName: "資料.pdf", ContentType: "application/pdf",
		ContentLength: uint64(len(content)), ContentDigest: "sha256:" + hex.EncodeToString(sum[:]),
	}
	download, err := service.OpenFrozenRecordAttachment(context.Background(), expected)
	if err != nil {
		t.Fatal(err)
	}
	if download.ContentType != expected.ContentType {
		t.Fatalf("frozen content type = %q, want record authority %q", download.ContentType, expected.ContentType)
	}
	got, err := io.ReadAll(download.Reader)
	_ = download.Reader.Close()
	if err != nil || string(got) != content {
		t.Fatalf("content=%q err=%v", got, err)
	}
	expected.ContentDigest = "sha256:" + strings.Repeat("0", 64)
	if _, err := service.OpenFrozenRecordAttachment(context.Background(), expected); err == nil {
		t.Fatal("metadata drift was accepted")
	}
}

func TestAuthorizeRecordAttachmentMetadataDeniesBeforeReturningMetadata(t *testing.T) {
	store := &keyedObjectStore{objects: map[string]string{"files/file-1": "unauthorized"}}
	records := &sourceFileRecordStore{
		files: map[string]models.SObject{
			"file-1": authorizedSystemFileRecord("parent-1", "files/file-1", "Hidden.pdf"),
		},
		parents: map[string]models.SObject{
			"Contract/parent-1": {constants.FieldID: "parent-1"},
		},
	}
	service := NewContractService(store, storage.ProviderFilesystem, "", "", records, denyFileReadPermissionChecker{}, nil)

	result, found, err := service.AuthorizeRecordAttachmentMetadata(context.Background(), "file-1", &models.UserSession{ID: "user-1"})
	if err == nil {
		t.Fatal("AuthorizeRecordAttachmentMetadata err = nil, want permission error")
	}
	if !found {
		t.Fatal("AuthorizeRecordAttachmentMetadata found = false, want true")
	}
	if result != nil {
		t.Fatalf("AuthorizeRecordAttachmentMetadata result = %#v, want nil", result)
	}
	if len(records.retrieved) != 0 {
		t.Fatalf("parent retrieves = %#v, want none after file-read denial", records.retrieved)
	}
	if len(store.getKeys) != 0 {
		t.Fatalf("storage gets = %#v, want none after file-read denial", store.getKeys)
	}
}

func TestResolveAuthorizedRecordAttachmentSourceMasksMatchedFileReadDenial(t *testing.T) {
	store := &keyedObjectStore{objects: map[string]string{"files/file-1": "hidden"}}
	records := &sourceFileRecordStore{
		files: map[string]models.SObject{
			"file-1": authorizedSystemFileRecord("parent-1", "files/file-1", "Hidden.pdf"),
		},
		parents: map[string]models.SObject{
			"Contract/parent-1": {constants.FieldID: "parent-1"},
		},
	}
	lookup := &recordAttachmentSourceLookupSpy{
		result: &contract.RecordAttachmentSourceLookupResult{FileID: "file-1"},
		found:  true,
		parentReads: func() int {
			return len(records.retrieved)
		},
	}
	service := NewContractService(store, storage.ProviderFilesystem, "", "", records, denyFileReadPermissionChecker{}, nil)

	result, found, err := service.ResolveAuthorizedRecordAttachmentSource(context.Background(), contract.RecordAttachmentSourceRequest{
		ParentObjectAPIName: "Contract",
		ParentID:            "parent-1",
		SourceObject:        "ProductSource",
		SourceField:         "WireBody",
		SourceValue:         "ABCDEF123456",
	}, lookup, &models.UserSession{ID: "user-1"})
	if err != nil {
		t.Fatalf("ResolveAuthorizedRecordAttachmentSource err = %v, want nil handled denial", err)
	}
	if !found {
		t.Fatal("ResolveAuthorizedRecordAttachmentSource found = false, want true")
	}
	if result != nil {
		t.Fatalf("ResolveAuthorizedRecordAttachmentSource result = %#v, want nil unavailable sentinel", result)
	}
	if len(lookup.calls) != 1 {
		t.Fatalf("lookup calls = %#v, want one matched-source lookup", lookup.calls)
	}
	if len(lookup.parentReadsAtCall) != 1 || lookup.parentReadsAtCall[0] == 0 {
		t.Fatalf("parent reads before lookup = %#v, want parent authorization before lookup", lookup.parentReadsAtCall)
	}
	if len(records.retrieved) != 1 || records.retrieved[0] != "Contract/parent-1" {
		t.Fatalf("parent retrieves = %#v, want one parent authorization read", records.retrieved)
	}
	if len(store.getKeys) != 0 {
		t.Fatalf("storage gets = %#v, want none", store.getKeys)
	}
}

func TestResolveAuthorizedRecordAttachmentSourceDeniesParentBeforeLookup(t *testing.T) {
	store := &keyedObjectStore{objects: map[string]string{"files/file-1": "hidden"}}
	records := &sourceFileRecordStore{
		files: map[string]models.SObject{
			"file-1": authorizedSystemFileRecord("parent-1", "files/file-1", "Hidden.pdf"),
		},
		parents: map[string]models.SObject{},
	}
	lookup := &recordAttachmentSourceLookupSpy{
		result: &contract.RecordAttachmentSourceLookupResult{FileID: "file-1"},
		found:  true,
	}
	service := NewContractService(store, storage.ProviderFilesystem, "", "", records, allowFilePermissionChecker{}, nil)

	result, found, err := service.ResolveAuthorizedRecordAttachmentSource(context.Background(), contract.RecordAttachmentSourceRequest{
		ParentObjectAPIName: "Contract",
		ParentID:            "parent-1",
		SourceObject:        "ProductSource",
		SourceField:         "WireBody",
		SourceValue:         "ABCDEF123456",
	}, lookup, &models.UserSession{ID: "user-1"})
	if err == nil {
		t.Fatal("ResolveAuthorizedRecordAttachmentSource err = nil, want parent authorization error")
	}
	if !found {
		t.Fatal("ResolveAuthorizedRecordAttachmentSource found = false, want true")
	}
	if result != nil {
		t.Fatalf("ResolveAuthorizedRecordAttachmentSource result = %#v, want nil", result)
	}
	if len(lookup.calls) != 0 {
		t.Fatalf("lookup calls = %#v, want none before parent authorization", lookup.calls)
	}
	if len(records.retrieved) != 1 || records.retrieved[0] != "Contract/parent-1" {
		t.Fatalf("parent retrieves = %#v, want Contract/parent-1", records.retrieved)
	}
	if len(store.getKeys) != 0 {
		t.Fatalf("storage gets = %#v, want none", store.getKeys)
	}
}

func TestResolveAuthorizedRecordAttachmentSourceAuthorizesLookupMetadataOnly(t *testing.T) {
	store := &keyedObjectStore{objects: map[string]string{"files/file-1": "authorized"}}
	records := &sourceFileRecordStore{
		files: map[string]models.SObject{
			"file-1": authorizedSystemFileRecord("parent-1", "files/file-1", "Authorized.pdf"),
		},
		parents: map[string]models.SObject{
			"Contract/parent-1": {constants.FieldID: "parent-1"},
		},
	}
	lookup := &recordAttachmentSourceLookupSpy{
		result: &contract.RecordAttachmentSourceLookupResult{FileID: "file-1"},
		found:  true,
		parentReads: func() int {
			return len(records.retrieved)
		},
	}
	service := NewContractService(store, storage.ProviderFilesystem, "", "", records, allowFilePermissionChecker{}, nil)

	result, found, err := service.ResolveAuthorizedRecordAttachmentSource(context.Background(), contract.RecordAttachmentSourceRequest{
		ParentObjectAPIName: "Contract",
		ParentID:            "parent-1",
		SourceObject:        "ProductSource",
		SourceField:         "WireBody",
		SourceValue:         "ABCDEF123456",
	}, lookup, &models.UserSession{ID: "user-1"})
	if err != nil {
		t.Fatalf("ResolveAuthorizedRecordAttachmentSource err = %v", err)
	}
	if !found {
		t.Fatal("ResolveAuthorizedRecordAttachmentSource found = false, want true")
	}
	if result == nil || result.FileID != "file-1" || result.FileName != "Authorized.pdf" {
		t.Fatalf("ResolveAuthorizedRecordAttachmentSource result = %#v", result)
	}
	if len(lookup.calls) != 1 {
		t.Fatalf("lookup calls = %#v, want one", lookup.calls)
	}
	if len(lookup.parentReadsAtCall) != 1 || lookup.parentReadsAtCall[0] == 0 {
		t.Fatalf("parent reads before lookup = %#v, want authorization before lookup", lookup.parentReadsAtCall)
	}
	if len(records.retrieved) != 2 || records.retrieved[0] != "Contract/parent-1" || records.retrieved[1] != "Contract/parent-1" {
		t.Fatalf("parent retrieves = %#v, want pre-lookup and metadata authorization reads", records.retrieved)
	}
	if len(store.getKeys) != 0 {
		t.Fatalf("storage gets = %#v, want none for metadata-only resolution", store.getKeys)
	}
}

func TestContractServiceDoesNotOwnSalesforceSourceLookup(t *testing.T) {
	body, err := os.ReadFile("contract_service.go")
	if err != nil {
		t.Fatalf("read source: %v", err)
	}
	source := string(body)
	for _, forbidden := range []string{"ContentDistribution", "SourceContentVersionId", "IdBody"} {
		if strings.Contains(source, forbidden) {
			t.Fatalf("contract_service.go contains %q; app-owned source lookup belongs behind RecordAttachmentSourceLookup", forbidden)
		}
	}
}

func authorizedSystemFileRecord(parentID, storageKey, name string) models.SObject {
	return models.SObject{
		constants.FieldID:                          "file-1",
		constants.FieldSysFile_ParentID:            parentID,
		constants.FieldSysFile_ParentObjectAPIName: "Contract",
		constants.FieldSysFile_OriginalFileName:    name,
		constants.FieldSysFile_Name:                name,
		constants.FieldSysFile_StorageProvider:     storage.ProviderFilesystem,
		constants.FieldSysFile_StorageBucket:       storage.DefaultFilesystemBucket,
		constants.FieldSysFile_StorageKey:          storageKey,
		constants.FieldSysFile_Status:              "Active",
		constants.FieldSysFile_SourceType:          systemFileSourceTypeHrefCompat,
		constants.FieldSysFile_SizeBytes:           int64(10),
		constants.FieldSysFile_MimeType:            "application/pdf",
		constants.FieldSysFile_Checksum:            "sha256:0000000000000000000000000000000000000000000000000000000000000000",
	}
}
