package dialectsfcrm

import (
	"slices"
	"testing"

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

// The create form supplies what the create path demands, whatever the layout says.
//
// A create form is built from a layout; the save is not — it demands what the
// schema demands. Any layout that omits one of those fields renders a form that
// cannot succeed, and the error names a field the user never saw. Two of
// thirty-five production layouts were in that state.
//
// These drive the projection rather than a handler, because the guarantee is
// about what is rendered. What they pin that a write-time check could not: the
// layout is repaired at the moment it is used, so layouts already stored wrong
// are covered, and so is the path that breaks them without writing a layout at
// all — flipping a field to required.

func supplyStub(t *testing.T, layoutFields []string) *uiAPITestContracts {
	t.Helper()
	stub := newUIAPITestContracts()
	stub.schema = &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldName, Label: "Account Name", Type: constants.FieldTypeString, Createable: true, Updateable: true, IsNameField: true},
			{APIName: "Rating__c", Label: "Rating", Type: constants.FieldTypeString, Createable: true, Updateable: true},
			{APIName: "Mandatory__c", Label: "Mandatory", Type: constants.FieldTypeString, Createable: true, Updateable: true, Required: true},
			// Required, but the platform fills it. NOTE this one is NOT a usable
			// discriminator on its own: the stub derives field access from the
			// schema, so Createable:false means the FLS gate rejects it before
			// the predicate is ever consulted. Kept because it is the realistic
			// shape of an autonumber; Defaulted__c below is what actually
			// separates the two predicates.
			{APIName: "Serial__c", Label: "Serial", Type: constants.FieldTypeAutonumber, Createable: false, Updateable: false, Required: true},
			// Required AND fully createable, so FLS cannot be the reason it
			// stays off the form — only the predicate can be. Without it a
			// supply built on the coarse `Required` flag, or with no predicate
			// at all, passes every test in this file.
			{APIName: "Defaulted__c", Label: "Defaulted", Type: constants.FieldTypeString, Createable: true, Updateable: true, Required: true, DefaultedOnCreate: true},
		},
	}
	stub.schemas = map[string]*models.ObjectMetadata{"Account": stub.schema}
	stub.layout = &models.PageLayout{
		ID: "curated_Account_default", ObjectAPIName: "Account", LayoutName: "Account Layout",
		Sections: []models.PageSection{{ID: "s1", Label: "Info", Columns: 2, Fields: layoutFields}},
	}
	return stub
}

func flatten(rows [][][]string) []string {
	var out []string
	for _, section := range rows {
		for _, row := range section {
			out = append(out, row...)
		}
	}
	return out
}

func TestCreateFormSuppliesADemandedFieldTheLayoutOmits(t *testing.T) {
	// The production shape: a stored layout that simply never carried it.
	stub := supplyStub(t, []string{constants.FieldName, "Rating__c"})

	got := flatten(layoutRows(t, stub, "Create"))

	if !slices.Contains(got, "Mandatory__c") {
		t.Fatalf("create form = %v, missing the field the save will demand", got)
	}
	// And nothing else. Two negative controls, both reachable — FLS is not the
	// reason either is absent:
	//   Defaulted__c is required and fully createable, exempt only because the
	//   create path fills it, so it separates this predicate from `Required`.
	//   Rating__c is unplaced and not required, so it catches a supply that
	//   dropped the predicate altogether and pushed every missing field on.
	if slices.Contains(got, "Defaulted__c") {
		t.Fatalf("create form = %v, supplied a field the create path defaults", got)
	}
	if slices.Contains(got, "Serial__c") {
		t.Fatalf("create form = %v, supplied a field the user cannot fill", got)
	}
}

func TestSupplyAddsNothingBeyondWhatIsDemanded(t *testing.T) {
	// The exact-set assertion the file was missing. Every other test asks
	// "contains", which cannot see a supply that adds more than it should.
	stub := supplyStub(t, []string{constants.FieldName})

	got := flatten(layoutRows(t, stub, "Create"))

	if !slices.Equal(got, []string{constants.FieldName, "Mandatory__c"}) {
		t.Fatalf("create form = %v, want the authored field plus exactly the demanded one", got)
	}
}

func TestTheOtherFormsShowExactlyWhatWasAuthored(t *testing.T) {
	// Supply is create-only. An update leaves an omitted field at its stored
	// value, so nothing is demanded there, and the record page must keep
	// showing what the admin arranged rather than a field they removed.
	stub := supplyStub(t, []string{constants.FieldName, "Rating__c"})

	for _, mode := range []string{"Detail", "Edit"} {
		got := flatten(layoutRows(t, stub, mode))
		if slices.Contains(got, "Mandatory__c") {
			t.Fatalf("%s form = %v, want only the authored fields", mode, got)
		}
	}
}

func TestASuppliedFieldIsNotDuplicatedWhenTheLayoutAlreadyHasIt(t *testing.T) {
	stub := supplyStub(t, []string{constants.FieldName, "Mandatory__c", "Rating__c"})

	got := flatten(layoutRows(t, stub, "Create"))

	count := 0
	for _, f := range got {
		if f == "Mandatory__c" {
			count++
		}
	}
	if count != 1 {
		t.Fatalf("create form = %v, want the demanded field exactly once", got)
	}
}

func TestSupplyKeepsTheAuthoredOrderAndAppends(t *testing.T) {
	stub := supplyStub(t, []string{constants.FieldName, "Rating__c"})

	got := flatten(layoutRows(t, stub, "Create"))

	// The admin's arrangement is not rearranged to make room; the field the
	// layout was missing goes on the end.
	if !slices.Equal(got, []string{constants.FieldName, "Rating__c", "Mandatory__c"}) {
		t.Fatalf("create form = %v, want the authored order with the missing field appended", got)
	}
}

func TestSupplyDoesNotOverrideFieldLevelSecurity(t *testing.T) {
	// FLS is the floor for everything this projection emits, and supply is not
	// an exception: a field the user may not create must not appear because the
	// schema demands it. The save will fail for them either way; showing a
	// control they cannot use would only move the confusion.
	for _, tc := range []struct {
		name   string
		access contract.FieldAccess
	}{
		{"unreadable", contract.FieldAccess{}},
		// The discriminating case. The section loop admits a readable field and
		// renders it non-editable, so a Readable-but-not-Createable field slips
		// past a check that only asks about Readable — which is what the first
		// version of this test did, and it passed against a supply that ignored
		// Createable entirely.
		{"readable but not createable", contract.FieldAccess{Readable: true}},
	} {
		t.Run(tc.name, func(t *testing.T) {
			stub := supplyStub(t, []string{constants.FieldName})
			stub.fieldAccess = map[string]contract.FieldAccess{"Mandatory__c": tc.access}

			got := flatten(layoutRows(t, stub, "Create"))
			if slices.Contains(got, "Mandatory__c") {
				t.Fatalf("create form = %v, supplied a field this user cannot fill", got)
			}
		})
	}
}

func TestSupplyStillReachesAUserWhoCanFillTheField(t *testing.T) {
	// The other side of the FLS check: tightening it must not turn supply off
	// for everyone, which a Createable check applied to the wrong operand would.
	stub := supplyStub(t, []string{constants.FieldName})
	// Createable but NOT updateable, which is the shape that matters here: a
	// parent lookup is set once at create and immutable after. Both production
	// layouts this work started from were missing exactly such a field
	// (OpportunityId is createable/non-updateable upstream too), so a check
	// that also demanded Updateable would refuse to supply the very fields the
	// feature exists for.
	stub.fieldAccess = map[string]contract.FieldAccess{
		"Mandatory__c": {Readable: true, Createable: true, Updateable: false},
	}

	got := flatten(layoutRows(t, stub, "Create"))
	if !slices.Contains(got, "Mandatory__c") {
		t.Fatalf("create form = %v, missing the field for a user who can fill it", got)
	}
}

func TestSupplyLeavesTheStoredLayoutAlone(t *testing.T) {
	// The layout may be shared with a cache or another request, and the detail
	// and edit forms must keep seeing what the admin authored.
	stub := supplyStub(t, []string{constants.FieldName, "Rating__c"})
	// Spare capacity, so appending to the stored slice would write into its
	// backing array instead of reallocating. Without this the fixture makes the
	// aliasing bug impossible to commit, and the assertion below proves nothing.
	//
	// A sentinel past the stored length is what makes the write observable: an
	// append into the shared array overwrites index 2 while the stored slice
	// still has length 2, so comparing the stored slice alone cannot see it.
	backing := make([]string, 3, 8)
	copy(backing, stub.layout.Sections[0].Fields)
	backing[2] = "sentinel"
	stub.layout.Sections[0].Fields = backing[:2]
	before := slices.Clone(backing[:2])

	_ = layoutRows(t, stub, "Create")

	if !slices.Equal(stub.layout.Sections[0].Fields, before) {
		t.Fatalf("stored layout mutated to %v, want %v", stub.layout.Sections[0].Fields, before)
	}
	if backing[2] != "sentinel" {
		t.Fatalf("wrote through the stored slice's backing array: index 2 is now %q", backing[2])
	}
}

// twoSectionStub puts the authored fields in a SECOND section and leaves the
// first empty, which is the shape every other fixture here lacks. Without it
// two rules go unpinned: which section receives the supplied field, and whether
// the "already placed" set is computed across all sections or only the first.
func twoSectionStub(t *testing.T, first, second []string) *uiAPITestContracts {
	t.Helper()
	stub := supplyStub(t, nil)
	stub.layout.Sections = []models.PageSection{
		{ID: "s1", Label: "Feed", Columns: 2, Fields: first},
		{ID: "s2", Label: "Details", Columns: 2, Fields: second},
	}
	return stub
}

func TestSupplyLandsWhereTheFieldsAre(t *testing.T) {
	// Section 0 holds nothing — a component or another tab's section. Dropping
	// a mandatory input there puts it somewhere nobody expects to type.
	stub := twoSectionStub(t, nil, []string{constants.FieldName})

	rows := layoutRows(t, stub, "Create")

	if len(rows) < 2 {
		t.Fatalf("want two sections, got %d", len(rows))
	}
	var s0, s1 []string
	for _, row := range rows[0] {
		s0 = append(s0, row...)
	}
	for _, row := range rows[1] {
		s1 = append(s1, row...)
	}
	if len(s0) != 0 {
		t.Fatalf("section 0 = %v, want the supplied field to go where the fields are", s0)
	}
	if !slices.Equal(s1, []string{constants.FieldName, "Mandatory__c"}) {
		t.Fatalf("section 1 = %v, want the authored field plus the supplied one", s1)
	}
}

func TestSupplyLandsInTheFirstFieldSectionNotTheLast(t *testing.T) {
	// Both sections hold fields, so "first with fields" and "last" are
	// different answers. With only one candidate section the two are
	// indistinguishable and the placement rule is unpinned.
	stub := twoSectionStub(t, []string{constants.FieldName}, []string{"Rating__c"})

	rows := layoutRows(t, stub, "Create")
	if len(rows) < 2 {
		t.Fatalf("want two sections, got %d", len(rows))
	}
	var s0 []string
	for _, row := range rows[0] {
		s0 = append(s0, row...)
	}
	if !slices.Equal(s0, []string{constants.FieldName, "Mandatory__c"}) {
		t.Fatalf("section 0 = %v, want the supplied field alongside the first section's own", s0)
	}
}

func TestSupplySeesFieldsPlacedInAnySection(t *testing.T) {
	// The demanded field is already placed, just not in the first section. A
	// "placed" set built from section 0 alone supplies it again and the create
	// form shows it twice.
	stub := twoSectionStub(t, []string{constants.FieldName}, []string{"Mandatory__c"})

	got := flatten(layoutRows(t, stub, "Create"))

	if !slices.Equal(got, []string{constants.FieldName, "Mandatory__c"}) {
		t.Fatalf("create form = %v, want each field exactly once", got)
	}
}

func TestSupplyGivesASectionlessLayoutSomewhereToPutIt(t *testing.T) {
	// A layout with no sections still renders a create form, and that form
	// still has to collect the demanded field. Returning it untouched was the
	// one place this abandoned its own invariant — and removing the guard that
	// did so used to panic on sections[0].
	stub := supplyStub(t, nil)
	stub.layout.Sections = nil

	got := flatten(layoutRows(t, stub, "Create"))

	if !slices.Contains(got, "Mandatory__c") {
		t.Fatalf("create form = %v, want the demanded field on a layout with no sections", got)
	}
}

func TestASectionlessLayoutDemandingNothingStaysEmpty(t *testing.T) {
	// And no section is invented when there is nothing to put in it.
	stub := supplyStub(t, nil)
	stub.layout.Sections = nil
	stub.schema.Fields = stub.schema.Fields[:2]
	stub.schemas = map[string]*models.ObjectMetadata{"Account": stub.schema}

	// layoutBody, not layoutRows: the latter type-asserts on `sections`, which
	// is exactly the strictness that makes it useful elsewhere and useless here.
	body := layoutBody(t, stub, "Create")
	if sections, present := body["sections"].([]any); present && len(sections) != 0 {
		t.Fatalf("sections = %v, want none invented", sections)
	}
}
