package sfcrmappservice

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

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

// New/edit are distinct public capabilities. In particular, granting insert
// without update must not hide a Runtime-produced default or make the defaults
// composition fail. A denied insert flag must not be widened by an edit grant.
// Salesforce field wire properties remain createable/updateable:
// https://developer.salesforce.com/docs/atlas.en-us.254.0.uiapi.meta/uiapi/ui_api_responses_field.htm
func TestRecordUIFieldCreateAuthorityIsIndependentOfEdit(t *testing.T) {
	for _, tc := range []struct {
		name                     string
		create, edit, objectEdit bool
	}{
		{name: "create_only", create: true},
		{name: "field_create_without_field_edit", create: true, objectEdit: true},
		{name: "field_edit_without_field_create", edit: true, objectEdit: true},
		{name: "denied"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			service := standaloneRecordUIMetadataTestService(t)
			read := service.permissionMetadata.readEffective
			service.permissionMetadata.readEffective = func(ctx context.Context, bearer string, request permissionsemantic.Request) (permissionsemantic.Response, error) {
				response, err := read(ctx, bearer, request)
				if err != nil {
					return response, err
				}
				for i := range response.ObjectPermissions {
					response.ObjectPermissions[i].CanEdit = tc.objectEdit
				}
				for i := range response.FieldPermissions {
					if response.FieldPermissions[i].FieldName == "SortOrder" {
						response.FieldPermissions[i].CanCreate = tc.create
						response.FieldPermissions[i].CanEdit = tc.edit
					}
				}
				return response, nil
			}
			preview := service.previewCreateDefaults
			service.previewCreateDefaults = func(ctx context.Context, bearer string, request defaultsemantic.Request) (defaultsemantic.Response, error) {
				response, err := preview(ctx, bearer, request)
				if err != nil {
					return response, err
				}
				if tc.create {
					value := int64(13)
					response.Fields = []defaultsemantic.Field{{Name: "SortOrder", Kind: defaultsemantic.ValueInteger, Integer: &value, Provenance: defaultsemantic.ProvenanceSiblingMaxPlusOne}}
				}
				return response, nil
			}
			for _, path := range []string{
				"/services/data/v63.0/ui-api/object-info/CampaignMemberStatus",
				"/services/data/v63.0/sobjects/CampaignMemberStatus/describe",
				"/services/data/v63.0/ui-api/layout/CampaignMemberStatus?type=Create",
				"/services/data/v63.0/ui-api/record-defaults/create/CampaignMemberStatus?parentFieldApiName=CampaignId&parentRecordId=campaign-1",
			} {
				response := getRecordUIMetadata(t, service, path)
				if response.Code != http.StatusOK {
					t.Fatalf("%s status=%d body=%s", path, response.Code, response.Body.String())
				}
				var body map[string]any
				if err := json.Unmarshal(response.Body.Bytes(), &body); err != nil {
					t.Fatal(err)
				}
				if strings.Contains(path, "/ui-api/layout/") {
					assertLayoutFieldCreateWire(t, body, tc.create, tc.edit && tc.objectEdit)
				}
				if layout, ok := body["layout"]; ok {
					assertLayoutFieldCreateWire(t, layout, tc.create, tc.edit && tc.objectEdit)
				}
				switch fields := body["fields"].(type) {
				case map[string]any:
					assertFieldCreateWire(t, path, fields["SortOrder"].(map[string]any), tc.create, tc.edit && tc.objectEdit)
				case []any:
					found := false
					for _, raw := range fields {
						field := raw.(map[string]any)
						if field["name"] == "SortOrder" {
							found = true
							assertFieldCreateWire(t, path, field, tc.create, tc.edit && tc.objectEdit)
						}
					}
					if !found {
						t.Fatalf("%s omitted readable field", path)
					}
				}
				if record, ok := body["record"].(map[string]any); ok {
					fields := record["fields"].(map[string]any)
					value, found := fields["SortOrder"]
					if found != tc.create {
						t.Fatalf("default presence=%v want=%v", found, tc.create)
					}
					if found && value.(map[string]any)["value"] != float64(13) {
						t.Fatalf("default=%v", value)
					}
					info := body["objectInfo"].(map[string]any)["fields"].(map[string]any)["SortOrder"].(map[string]any)
					assertFieldCreateWire(t, path, info, tc.create, tc.edit && tc.objectEdit)
				}
			}
		})
	}
}

func assertFieldCreateWire(t *testing.T, path string, field map[string]any, create, update bool) {
	t.Helper()
	if field["createable"] != create || field["updateable"] != update {
		t.Fatalf("%s createable/updateable=%v/%v want=%v/%v", path, field["createable"], field["updateable"], create, update)
	}
}

func assertLayoutFieldCreateWire(t *testing.T, value any, create, update bool) {
	t.Helper()
	found := false
	var visit func(any)
	visit = func(value any) {
		switch v := value.(type) {
		case map[string]any:
			if components, ok := v["layoutComponents"].([]any); ok {
				for _, raw := range components {
					if component, ok := raw.(map[string]any); ok && component["apiName"] == "SortOrder" {
						found = true
						if v["editableForNew"] != create || v["editableForUpdate"] != update {
							t.Fatalf("layout field=%v", v)
						}
					}
				}
			}
			for _, child := range v {
				visit(child)
			}
		case []any:
			for _, child := range v {
				visit(child)
			}
		}
	}
	visit(value)
	if !found {
		t.Fatal("layout omitted readable SortOrder")
	}
}
