package catalogsnapshot

import (
	"errors"
	"testing"

	"github.com/nexuscrm/shared/pkg/models"
)

func TestPageLayoutResolutionUsesCallerDimensionsAndNearestRoleAncestor(t *testing.T) {
	input := pageLayoutResolutionCompileInput()
	snapshot := mustOpenSnapshot(t, mustCompile(t, input))
	artifact, err := ProjectRuntimeMetadataView(snapshot, ProjectionKey{
		AppInstallationID: testInstallA,
		CapabilityID:      "runtime.metadata.resolve-page-layout.v1",
	})
	if err != nil {
		t.Fatal(err)
	}
	view := mustOpenView(t, artifact)
	if !view.SupportsPageLayoutResolutionServing() || len(view.Objects()) != 0 {
		t.Fatalf("page-layout view serving=%v objects=%d", view.SupportsPageLayoutResolutionServing(), len(view.Objects()))
	}

	tests := []struct {
		name, profile, recordType string
		roles                     []string
		want                      string
	}{
		{name: "wildcard", want: "layout-default"},
		{name: "profile", profile: "profile-sales", want: "layout-profile"},
		{name: "record type", recordType: "rt-enterprise", want: "layout-record-type"},
		{name: "profile and record type", profile: "profile-sales", recordType: "rt-enterprise", want: "layout-profile-record-type"},
		{name: "nearest role ancestor", roles: []string{"role-child", "role-near", "role-far"}, want: "layout-role-near"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			layout, related, err := view.ResolvePageLayout("WorkItem", test.profile, test.roles, test.recordType)
			if err != nil {
				t.Fatal(err)
			}
			if layout.ID != test.want {
				t.Fatalf("layout = %q, want %q", layout.ID, test.want)
			}
			if len(related) != 1 || related[0].DisplayObjectID != "Unrelated" {
				t.Fatalf("related-list resolutions = %#v", related)
			}
		})
	}
	if _, _, err := view.ResolvePageLayout("WorkItem", "", nil, "rt-unknown"); !errors.Is(err, ErrPageLayoutRecordTypeMissing) {
		t.Fatalf("unknown RecordType error = %v", err)
	}
}

func TestPageLayoutResolutionCompilerRejectsMixedRevisionAndOrphanAssignment(t *testing.T) {
	t.Run("mixed revision", func(t *testing.T) {
		input := pageLayoutResolutionCompileInput()
		input.PageLayouts[0].CatalogRevision++
		if _, err := CompileCatalogSnapshot(input); err == nil {
			t.Fatal("mixed-revision layout compiled")
		}
	})
	t.Run("orphan assignment", func(t *testing.T) {
		input := pageLayoutResolutionCompileInput()
		input.LayoutAssignments[0].Assignment.LayoutID = "layout-missing"
		if _, err := CompileCatalogSnapshot(input); err == nil {
			t.Fatal("orphan assignment compiled")
		}
	})
	t.Run("duplicate dimensions", func(t *testing.T) {
		input := pageLayoutResolutionCompileInput()
		duplicate := *input.LayoutAssignments[0].Assignment
		duplicate.ID = "assignment-duplicate"
		input.LayoutAssignments = append(input.LayoutAssignments, LayoutAssignmentInput{CatalogRevision: testRevision, Assignment: &duplicate})
		if _, err := CompileCatalogSnapshot(input); err == nil {
			t.Fatal("duplicate assignment dimensions compiled")
		}
	})
	t.Run("assignment record type is absent from closure", func(t *testing.T) {
		input := pageLayoutResolutionCompileInput()
		input.PageLayoutRecordTypes = nil
		if _, err := CompileCatalogSnapshot(input); err == nil {
			t.Fatal("assignment with an unpublished record type compiled")
		}
	})
}

func pageLayoutResolutionCompileInput() CompileInput {
	input := neutralCompileInput()
	input.Projections = []RuntimeProjectionInput{{
		CatalogRevision: testRevision, AppInstallationID: testInstallA,
		CapabilityID:   "runtime.metadata.resolve-page-layout.v1",
		Profile:        ProjectionPageLayoutResolutionV1,
		ObjectAPINames: []string{"Unrelated", "WorkItem", "Workspace"},
	}}
	layouts := []*models.PageLayout{
		pageLayoutTestLayout("layout-default"),
		pageLayoutTestLayout("layout-profile"),
		pageLayoutTestLayout("layout-record-type"),
		pageLayoutTestLayout("layout-profile-record-type"),
		pageLayoutTestLayout("layout-role-near"),
		pageLayoutTestLayout("layout-role-far"),
	}
	for _, layout := range layouts {
		input.PageLayouts = append(input.PageLayouts, PageLayoutInput{CatalogRevision: testRevision, Layout: layout})
	}
	profile := "profile-sales"
	recordType := "rt-enterprise"
	nearRole := "role-near"
	farRole := "role-far"
	assignments := []*models.LayoutAssignment{
		{ID: "assignment-default", ObjectAPIName: "WorkItem", LayoutID: "layout-default", Specificity: 0},
		{ID: "assignment-profile", ObjectAPIName: "WorkItem", LayoutID: "layout-profile", ProfileID: &profile, Specificity: 1},
		{ID: "assignment-record-type", ObjectAPIName: "WorkItem", LayoutID: "layout-record-type", RecordTypeID: &recordType, Specificity: 1},
		{ID: "assignment-profile-record-type", ObjectAPIName: "WorkItem", LayoutID: "layout-profile-record-type", ProfileID: &profile, RecordTypeID: &recordType, Specificity: 2},
		{ID: "assignment-role-near", ObjectAPIName: "WorkItem", LayoutID: "layout-role-near", RoleID: &nearRole, Specificity: 1},
		{ID: "assignment-role-far", ObjectAPIName: "WorkItem", LayoutID: "layout-role-far", RoleID: &farRole, Specificity: 1},
	}
	for _, assignment := range assignments {
		input.LayoutAssignments = append(input.LayoutAssignments, LayoutAssignmentInput{CatalogRevision: testRevision, Assignment: assignment})
	}
	input.PageLayoutRecordTypes = []PageLayoutRecordTypeInput{{
		CatalogRevision: testRevision, ObjectAPIName: "WorkItem", RecordTypeID: recordType,
	}}
	input.RelatedListResolutions = []RelatedListResolutionInput{{
		CatalogRevision: testRevision, ParentObjectID: "WorkItem",
		DisplayObjectID: "Unrelated", DisplayLookupField: "WorkItemId",
		Query:      PageLayoutResolvedQueryTarget{ObjectID: "Unrelated", LookupField: "WorkItemId"},
		Detail:     PageLayoutResolvedDetailTarget{ObjectID: "Unrelated"},
		ListTarget: PageLayoutResolvedListTarget{ObjectID: "Unrelated", FilterField: "WorkItemId"},
		Create:     PageLayoutResolvedCreateTarget{Kind: "standard", ObjectID: "Unrelated", PrefillMappings: map[string]string{"WorkItemId": "Id"}},
	}}
	return input
}

func pageLayoutTestLayout(id string) *models.PageLayout {
	return &models.PageLayout{
		ID: id, ObjectAPIName: "WorkItem", LayoutName: id,
		CompactLayout: []string{"Subject"}, Sections: []models.PageSection{},
		RelatedLists: []models.RelatedListConfig{{
			ID: "related-unrelated", Label: "Unrelated", ObjectAPIName: "Unrelated",
			LookupField: "WorkItemId", Fields: []string{"Id"},
		}},
		HeaderActions: []models.ActionConfig{}, QuickActions: []models.ActionConfig{},
	}
}
