package sfcrmappservice

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

	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
	defaultsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recorddefaultsv1"
)

// Fixtures explicitly supply the producer-owned requirement list. This helper
// changes only the component version; it does not infer rules from field flags.
func createInputSearchTestView(t *testing.T, objects []searchObjectSpec) catalogsemantic.AppMetadataViewV3 {
	t.Helper()
	view := searchTestView(t, objects)
	view.EffectiveComponents[0].ComponentFormatVersion = effectiveObjectSearchComponentFormatV6
	view.Lineage.ViewDigest = catalogsemantic.ComputeAppMetadataViewV3Digest(view)
	return view
}

func TestObjectSearchCreateInputContextVersionAndPresence(t *testing.T) {
	for _, test := range []struct {
		name    string
		format  uint32
		context string
		wantErr bool
	}{
		{"current required", 6, `,"createRequiredFields":["Title"]`, false},
		{"current empty", 6, `,"createRequiredFields":[]`, false},
		{"current missing", 6, "", true},
		{"current null", 6, `,"createRequiredFields":null`, true},
		{"unknown field", 6, `,"createRequiredFields":["Absent"]`, true},
		{"wrong casing", 6, `,"createRequiredFields":["title"]`, true},
		{"duplicate", 6, `,"createRequiredFields":["Title","Title"]`, true},
		{"unsorted", 6, `,"createRequiredFields":["Title","Other"]`, true},
		{"historical unknown", 5, "", false},
		{"historical cannot claim context", 5, `,"createRequiredFields":[]`, true},
	} {
		t.Run(test.name, func(t *testing.T) {
			behavior := []byte(`{"objects":[{"apiName":"WorkItem","label":"Work item","tableType":"standard_object","fields":[{"apiName":"Title","label":"Title","type":"string","required":true,"createable":true,"updateable":true},{"apiName":"Other","label":"Other","type":"string","createable":true,"updateable":true}]` + test.context + `}]}`)
			specs, err := searchObjectSpecsFromView(catalogsemantic.AppMetadataViewV3{EffectiveComponents: []catalogsemantic.EffectiveMetadataComponent{{
				ComponentKind: catalogsemantic.EffectiveObjectSearchComponentKind, BusinessKey: catalogsemantic.EffectiveObjectSearchComponentBusinessKey,
				ComponentFormatVersion: test.format, CanonicalBehavior: behavior,
			}}})
			if (err != nil) != test.wantErr {
				t.Fatalf("decode error = %v, wantErr %v", err, test.wantErr)
			}
			if err == nil && (specs["workitem"].CreateRequiredFields != nil) != (test.format == 6) {
				t.Fatalf("context presence changed: %#v", specs["workitem"].CreateRequiredFields)
			}
		})
	}
}

func TestRecordUICreateClampConsumesProducerContextWithoutChangingWireRequired(t *testing.T) {
	tableType := "standard_object"
	spec := searchObjectSpec{APIName: "WorkItem", TableType: &tableType, CreateRequiredFields: &[]string{"Omitted", "Title"}, Fields: []searchFieldSpec{
		{APIName: "Title", Label: "Title", Type: "string", Required: true, Createable: true, Updateable: true},
		{APIName: "Defaulted", Label: "Defaulted", Type: "string", Required: true, DefaultedOnCreate: true, Createable: true, Updateable: true},
		{APIName: "Omitted", Label: "Omitted", Type: "string", Required: true, Createable: true, Updateable: true},
	}}
	permissions := permissionsemantic.Response{FieldPermissions: []permissionsemantic.FieldPermission{
		{ResourceType: "WorkItem", FieldName: "Title", CanRead: true, CanEdit: true, CanCreate: true},
		{ResourceType: "WorkItem", FieldName: "Defaulted", CanRead: true, CanEdit: true, CanCreate: true},
		{ResourceType: "WorkItem", FieldName: "Omitted", CanRead: true, CanEdit: true, CanCreate: true},
	}}
	layout := appPageLayout{ObjectAPIName: "WorkItem", Sections: []appPageLayoutSection{{ID: "main", Columns: 1, Fields: []string{"Title", "Defaulted"}, FieldBehaviors: map[string]map[string]string{
		"Title": {"create": "hidden", "edit": "hidden", "detail": "hidden"}, "Defaulted": {"create": "hidden", "edit": "hidden", "detail": "hidden"},
	}}}}
	project := func(mode string) ([]map[string]any, error) {
		projected, err := projectRecordUILayout(layout, spec, newPageLayoutAuthorityIndex(map[string]searchObjectSpec{"workitem": spec}, permissions), permissionsemantic.ObjectPermission{ResourceType: "WorkItem", CanCreate: true, CanRead: true, CanEdit: true}, permissions, mode)
		if err != nil {
			return nil, err
		}
		var items []map[string]any
		for _, section := range projected["sections"].([]map[string]any) {
			for _, row := range section["layoutRows"].([]map[string]any) {
				items = append(items, row["layoutItems"].([]map[string]any)...)
			}
		}
		return items, nil
	}
	items, err := project("Create")
	if err != nil || len(items) != 1 || items[0]["layoutComponents"].([]map[string]any)[0]["apiName"] != "Title" || items[0]["required"] != true {
		t.Fatalf("Create items = %#v, error = %v", items, err)
	}
	layout.Sections[0].FieldBehaviors["Defaulted"]["create"] = "shown"
	items, err = project("Create")
	if err != nil || len(items) != 2 || items[1]["required"] != true {
		t.Fatalf("defaulted wire Required changed: %#v, error = %v", items, err)
	}
	// Historical metadata is unknown, not an empty set or raw-Required fallback.
	spec.CreateRequiredFields = nil
	if _, err := project("Create"); err == nil {
		t.Fatal("Create projected without current requirement facts")
	}
	for _, mode := range []string{"Edit", "View"} {
		items, err := project(mode)
		if err != nil || len(items) != 0 {
			t.Fatalf("historical %s items = %#v, error = %v", mode, items, err)
		}
	}
	spec.CreateRequiredFields = &[]string{}
	if _, err := project("Create"); err != nil {
		t.Fatalf("known-empty context rejected: %v", err)
	}
}

func TestHistoricalObjectMetadataKeepsViewAndEditButRejectsCreateBeforeDefaultsCall(t *testing.T) {
	service := standaloneRecordUIMetadataTestService(t)
	read := service.appMetadataV3
	service.appMetadataV3 = func(ctx context.Context, request catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
		view, err := read(ctx, request)
		if err != nil {
			return view, err
		}
		view.EffectiveComponents = append([]catalogsemantic.EffectiveMetadataComponent(nil), view.EffectiveComponents...)
		for index := range view.EffectiveComponents {
			component := &view.EffectiveComponents[index]
			if component.ComponentKind != catalogsemantic.EffectiveObjectSearchComponentKind {
				continue
			}
			var document effectiveObjectSearchDocument
			if err := json.Unmarshal(component.CanonicalBehavior, &document); err != nil {
				t.Fatal(err)
			}
			for index := range document.Objects {
				document.Objects[index].CreateRequiredFields = nil
			}
			component.CanonicalBehavior, err = json.Marshal(document)
			if err != nil {
				t.Fatal(err)
			}
			component.ComponentFormatVersion = effectiveObjectSearchComponentFormatV5
			component.ContentDigest = catalogsemantic.ComputeEffectiveMetadataComponentDigest(component.CanonicalBehavior)
		}
		view.Lineage.ViewDigest = catalogsemantic.ComputeAppMetadataViewV3Digest(view)
		return view, nil
	}
	defaultsCalls := 0
	preview := service.previewCreateDefaults
	service.previewCreateDefaults = func(ctx context.Context, bearer string, request defaultsemantic.Request) (defaultsemantic.Response, error) {
		defaultsCalls++
		return preview(ctx, bearer, request)
	}
	for _, test := range []struct {
		path   string
		status int
	}{
		{"/services/data/v63.0/ui-api/layout/CampaignMember?type=View", http.StatusOK},
		{"/services/data/v63.0/ui-api/layout/CampaignMember?type=Edit", http.StatusOK},
		{"/services/data/v63.0/ui-api/layout/CampaignMember?type=Create", http.StatusServiceUnavailable},
		{"/services/data/v63.0/ui-api/record-defaults/create/CampaignMember", http.StatusServiceUnavailable},
	} {
		out := getRecordUIMetadata(t, service, test.path)
		if out.Code != test.status {
			t.Fatalf("%s = %d %s, want %d", test.path, out.Code, out.Body.String(), test.status)
		}
	}
	if defaultsCalls != 0 {
		t.Fatalf("unknown Create context performed %d defaults calls", defaultsCalls)
	}
}
