package sfcrmappservice

import (
	"context"
	"encoding/json"
	"net/http"
	"slices"
	"testing"

	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
	pagelayoutsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/pagelayoutresolutionv1"
	attachmentsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordattachmentsv1"
	defaultsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recorddefaultsv1"
	recordsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordviewv1"
)

func TestStandaloneCloneDefaultsUsesCreateAuthorityAndSourceRecord(t *testing.T) {
	service := standaloneRecordUIMetadataTestService(t)
	view, err := service.appMetadataV3(context.Background(), catalogsemantic.GetRequest{})
	if err != nil {
		t.Fatal(err)
	}
	receipts := view.Receipts
	receipts.CapabilityVersion = recordsemantic.CapabilityName
	var readRequest recordsemantic.ReadRequest
	allowSourceRead := true
	service.recordView, err = NewRuntimeRecordViewClient(func(
		_ context.Context,
		bearer string,
		request recordsemantic.ReadRequest,
	) (recordsemantic.ReadResponse, error) {
		if bearer != "end-user" {
			t.Fatalf("bearer=%q", bearer)
		}
		if !allowSourceRead {
			return recordsemantic.ReadResponse{}, &recordsemantic.Failure{
				Category: recordsemantic.FailureForbidden,
			}
		}
		readRequest = request
		id, name, secret := request.Selector.RecordID, "Acme", "must-not-leak"
		recordTypeID := masterRecordTypeID
		return recordsemantic.ReadResponse{
			RecordID: request.Selector.RecordID,
			Root: recordsemantic.Record{ObjectID: "Account", Fields: []recordsemantic.Field{
				{Name: "Id", Value: recordsemantic.Value{Kind: recordsemantic.ValueString, String: &id}},
				{Name: "Name", Value: recordsemantic.Value{Kind: recordsemantic.ValueString, String: &name}},
				{Name: "RecordTypeId", Value: recordsemantic.Value{Kind: recordsemantic.ValueReference, Reference: &recordTypeID}},
				{Name: "Secret__c", Value: recordsemantic.Value{Kind: recordsemantic.ValueString, String: &secret}},
			}},
			Access:   &recordsemantic.RecordAccess{Read: true},
			Receipts: receipts,
		}, nil
	}, func(context.Context, string, attachmentsemantic.ResolveRequest) (attachmentsemantic.ResolveResponse, error) {
		return attachmentsemantic.ResolveResponse{}, nil
	})
	if err != nil {
		t.Fatal(err)
	}
	authorizeCalls := 0
	service.authorizeRuntime = func(ctx context.Context, bearer string, capabilities []string) (context.Context, error) {
		authorizeCalls++
		expected := []string{
			defaultsemantic.CapabilityName,
			pagelayoutsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
			recordsemantic.CapabilityName,
		}
		slices.Sort(expected)
		if bearer != "end-user" || !slices.Equal(capabilities, expected) {
			t.Fatalf("authorization bearer=%q capabilities=%#v", bearer, capabilities)
		}
		return ctx, nil
	}
	response := getRecordUIMetadata(t, service,
		"/services/data/v63.0/ui-api/record-defaults/clone/account-1?type=Account")
	if response.Code != http.StatusOK {
		t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
	}
	if readRequest.ObjectID != "Account" || readRequest.Selector.RecordID != "account-1" ||
		readRequest.Projection != recordsemantic.ProjectionEnrichedView || authorizeCalls != 1 {
		t.Fatalf("record read=%#v", readRequest)
	}
	var body struct {
		ObjectAPIName string `json:"objectApiName"`
		RecordTypeID  string `json:"recordTypeId"`
		Record        struct {
			Fields map[string]struct {
				Value any `json:"value"`
			} `json:"fields"`
		} `json:"record"`
		Layout struct {
			Mode string `json:"mode"`
		} `json:"layout"`
	}
	if err := json.Unmarshal(response.Body.Bytes(), &body); err != nil {
		t.Fatal(err)
	}
	if body.ObjectAPIName != "Account" || body.RecordTypeID != masterRecordTypeID || body.Layout.Mode != "Create" ||
		body.Record.Fields["Name"].Value != "Acme" {
		t.Fatalf("clone defaults=%#v", body)
	}
	if _, leaked := body.Record.Fields["Secret__c"]; leaked {
		t.Fatalf("unreadable field leaked: %#v", body.Record.Fields)
	}

	inferred := getRecordUIMetadata(t, service,
		"/services/data/v63.0/ui-api/record-defaults/clone/001000000000001AAA")
	if inferred.Code != http.StatusOK || readRequest.ObjectID != "Account" ||
		readRequest.Selector.RecordID != "001000000000001AAA" {
		t.Fatalf("inferred type status=%d read=%#v body=%s", inferred.Code, readRequest, inferred.Body.String())
	}

	for _, opaque := range []string{
		"999000000000001AAA",
		"00100000-0000-4000-8000-000000000000",
	} {
		unresolved := getRecordUIMetadata(t, service,
			"/services/data/v63.0/ui-api/record-defaults/clone/"+opaque)
		if unresolved.Code != http.StatusNotFound {
			t.Fatalf("unresolved %q status=%d body=%s", opaque, unresolved.Code, unresolved.Body.String())
		}
	}

	allowSourceRead = false
	denied := getRecordUIMetadata(t, service,
		"/services/data/v63.0/ui-api/record-defaults/clone/account-2?type=Account")
	if denied.Code != http.StatusForbidden {
		t.Fatalf("source read denied status=%d body=%s", denied.Code, denied.Body.String())
	}
}

func TestApplyCloneSourceFieldsDoesNotCopyAttachmentProviderIdentity(t *testing.T) {
	response := map[string]any{
		"record": map[string]any{
			"fields": map[string]any{
				"Name":          map[string]any{"value": nil, "displayValue": nil},
				"LegacyLink__c": map[string]any{"value": nil, "displayValue": nil},
			},
		},
	}
	name := "Acme"
	privateHref := "https://private-storage.invalid/object"
	read := recordsemantic.ReadResponse{
		Root: recordsemantic.Record{Fields: []recordsemantic.Field{
			{Name: "Name", Value: recordsemantic.Value{Kind: recordsemantic.ValueString, String: &name}},
			{Name: "LegacyLink__c", Value: recordsemantic.Value{Kind: recordsemantic.ValueString, String: &privateHref}},
		}},
		AttachmentSourceFields: []recordsemantic.AttachmentSourceField{{
			SourceFieldID: "legacylink__C",
			ResolverID:    "content_distribution",
		}},
	}

	if err := applyCloneSourceFields(response, read); err != nil {
		t.Fatal(err)
	}
	fields := response["record"].(map[string]any)["fields"].(map[string]any)
	if got := fields["Name"].(map[string]any)["value"]; got != "Acme" {
		t.Fatalf("ordinary clone value=%#v, want Acme", got)
	}
	if got := fields["LegacyLink__c"].(map[string]any)["value"]; got != nil {
		t.Fatalf("attachment provider identity leaked into clone defaults: %#v", got)
	}
}

func TestCloneObjectInferenceRejectsStaleCatalogSpecs(t *testing.T) {
	tableType := "Standard"
	specs := map[string]searchObjectSpec{
		"current": {APIName: "CurrentObject", KeyPrefix: "001", TableType: &tableType},
		"stale":   {APIName: "StaleObject", KeyPrefix: "002"},
	}

	current, err := cloneObjectSpecForRecordID(specs, "001000000000001AAA")
	if err != nil || current.APIName != "CurrentObject" {
		t.Fatalf("current inference=%#v err=%v", current, err)
	}
	if _, err := cloneObjectSpecForRecordID(specs, "002000000000001AAA"); err == nil {
		t.Fatal("stale Catalog spec was accepted for clone object inference")
	}
}
