package sfcrmappservice

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

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

func TestSearchScopesUseInstalledObjectsInsteadOfReleaseDefaults(t *testing.T) {
	tableType := "custom_object"
	searchable, hidden := true, false
	view := searchTestView(t, []searchObjectSpec{
		{APIName: "Account", Label: "Account", KeyPrefix: "001", TableType: &tableType, Searchable: &searchable},
		{APIName: "Workspace__c", Label: "Workspace", KeyPrefix: "a01", TableType: &tableType, Searchable: &searchable},
		{APIName: "Collection__c", Label: "Collection", TableType: &tableType, Searchable: &searchable},
		{APIName: "Private__c", Label: "Private", TableType: &tableType, Searchable: &searchable},
		{APIName: "Hidden__c", Label: "Hidden", TableType: &tableType, Searchable: &hidden},
	})
	behavior := []byte(`[{"name":"Workspace__c","label":"Old label"},{"name":"Account","label":"Account"},{"name":"Collection__c","label":"Collection"},{"name":"Private__c","label":"Private"},{"name":"Hidden__c","label":"Hidden"}]`)
	view.EffectiveComponents = append(view.EffectiveComponents, catalogsemantic.EffectiveMetadataComponent{
		ComponentKind: catalogsemantic.EffectiveSearchScopeComponentKind, ComponentFormatVersion: 1,
		BusinessKey: catalogsemantic.EffectiveSearchScopeComponentBusinessKey, CanonicalBehavior: behavior,
		ContentDigest: catalogsemantic.ComputeEffectiveMetadataComponentDigest(behavior),
	})
	view.Lineage.ViewDigest = catalogsemantic.ComputeAppMetadataViewV3Digest(view)
	// AppComponents still contain only the old Account/Campaign/Lead fixture.
	// L2-only types must survive; the default release cannot be consulted.
	for _, path := range []string{"metadata/search/scope-order", "search/scopeOrder"} {
		t.Run(path, func(t *testing.T) {
			reads, decisions := 0, 0
			config := permissionMetadataServiceConfig(func(_ context.Context, _ string, request permissionsemantic.Request) (permissionsemantic.Response, error) {
				decisions++
				if request.MinCatalogRevision == nil || *request.MinCatalogRevision != 17 || request.Selection == nil || !slices.Equal(request.Selection.ResourceTypes, []string{"Account", "Collection__c", "Private__c", "Workspace__c"}) {
					t.Fatalf("permission request = %+v", request)
				}
				response := validPermissionResponse(17, 3)
				response.ObjectPermissions = []permissionsemantic.ObjectPermission{{ResourceType: "Account", CanRead: true}, {ResourceType: "Collection__c", CanRead: true}, {ResourceType: "Private__c", CanRead: false}, {ResourceType: "Workspace__c", CanRead: true}}
				return selectedPermissionTestResponse(response, request), nil
			}, nil)
			config.RuntimeGetAppMetadataViewV3 = func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
				reads++
				return view, nil
			}
			config.RuntimeReadSearchScopeOrder = func(_ context.Context, _ string, request searchsemantic.ScopeOrderRequest) (searchsemantic.ScopeOrderResponse, error) {
				decisions++
				if request.MinCatalogRevision == nil || *request.MinCatalogRevision != 17 || !slices.Equal(request.ObjectIDs, []string{"Workspace__c", "Account", "Collection__c", "Private__c"}) {
					t.Fatalf("scope request=%+v", request)
				}
				return validSearchScopeResponse(17, 3, "Workspace__c", "Account", "Collection__c"), nil
			}
			service, err := NewService(config)
			if err != nil {
				t.Fatal(err)
			}
			recorder := httptest.NewRecorder()
			service.ServeHTTP(recorder, permissionMetadataRequest(http.MethodGet, "/services/data/v63.0/"+path))
			if recorder.Code != http.StatusOK || reads != 1 || decisions != 1 {
				t.Fatalf("status=%d reads=%d decisions=%d body=%s", recorder.Code, reads, decisions, recorder.Body.String())
			}
			if path == "metadata/search/scope-order" {
				if recorder.Body.String() != `["Workspace__c","Account","Collection__c"]`+"\n" {
					t.Fatal(recorder.Body.String())
				}
			} else {
				var result []searchScopeOrderItemV63
				if err := json.Unmarshal(recorder.Body.Bytes(), &result); err != nil {
					t.Fatal(err)
				}
				if len(result) != 3 || result[0].Name != "Workspace__c" || result[0].Label != "Workspace" || result[0].KeyPrefix != "a01" || strings.Contains(recorder.Body.String(), "Private__c") {
					t.Fatalf("result=%+v", result)
				}
			}
			// Removing effective scope must fail, even though L1 defaults remain valid.
			missing := view
			missing.EffectiveComponents = missing.EffectiveComponents[:1]
			missing.Lineage.ViewDigest = catalogsemantic.ComputeAppMetadataViewV3Digest(missing)
			config.RuntimeGetAppMetadataViewV3 = func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
				return missing, nil
			}
			service, err = NewService(config)
			if err != nil {
				t.Fatal(err)
			}
			recorder = httptest.NewRecorder()
			service.ServeHTTP(recorder, permissionMetadataRequest(http.MethodGet, "/services/data/v63.0/"+path))
			if recorder.Code != http.StatusServiceUnavailable || decisions != 1 {
				t.Fatalf("missing scope fallback: status=%d decisions=%d body=%s", recorder.Code, decisions, recorder.Body.String())
			}
		})
	}
}
