package catalogreconcile

import (
	"bytes"
	"context"
	"encoding/json"
	"strings"
	"testing"

	"github.com/nexuscrm/shared/pkg/models"
	consumerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
)

type effectivePageLayoutReaderStub struct {
	layouts map[string][]*models.PageLayout
}

func (stub effectivePageLayoutReaderStub) GetLayouts(
	_ context.Context,
	objectName string,
) ([]*models.PageLayout, error) {
	return stub.layouts[objectName], nil
}

func pageLayoutBehavior(t *testing.T, layout models.PageLayout) []byte {
	t.Helper()
	encoded, err := json.Marshal(layout)
	if err != nil {
		t.Fatal(err)
	}
	return encoded
}

func TestBuildEffectivePageLayoutComponentsPublishesAuthorizedOverride(t *testing.T) {
	base := models.PageLayout{
		ObjectAPIName: "Account", LayoutName: "Account Layout", IsDefault: true,
		Sections: []models.PageSection{{ID: "details", Label: "Details", Fields: []string{"Name"}}},
	}
	release := verifiedPlatformRelease(t, []platformReleaseComponentFixture{{
		kind: appReleasePageLayoutComponentKind, businessKey: "Account/default",
		behavior: pageLayoutBehavior(t, base),
	}})
	override := base
	override.ID = "curated_Account_default"
	override.Source = "curated"
	override.Sections = []models.PageSection{{ID: "details", Label: "Details", Fields: []string{"Name", "OwnerId"}}}
	components, err := buildEffectivePageLayoutComponents(
		context.Background(),
		effectivePageLayoutReaderStub{layouts: map[string][]*models.PageLayout{
			"Account": {&override},
		}},
		&release,
		nil,
	)
	if err != nil {
		t.Fatal(err)
	}
	if len(components) != 1 ||
		components[0].ComponentKind != consumerv1.EffectivePageLayoutComponentKind ||
		components[0].BusinessKey != "Account/default" {
		t.Fatalf("effective components = %#v", components)
	}
	want, err := canonicalPageLayoutModel(&override)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(components[0].CanonicalBehavior, want) {
		t.Fatalf("effective page layout = %s, want %s", components[0].CanonicalBehavior, want)
	}
}

func TestBuildEffectivePageLayoutComponentsOmitsUnchangedLayout(t *testing.T) {
	base := models.PageLayout{
		ObjectAPIName: "Lead", LayoutName: "Lead Layout", IsDefault: true,
		Sections: []models.PageSection{{ID: "details", Label: "Details", Fields: []string{"Name"}}},
	}
	release := verifiedPlatformRelease(t, []platformReleaseComponentFixture{{
		kind: appReleasePageLayoutComponentKind, businessKey: "Lead/default",
		behavior: pageLayoutBehavior(t, base),
	}})
	effective := base
	effective.ID = "curated_Lead_default"
	effective.Source = "curated"
	components, err := buildEffectivePageLayoutComponents(
		context.Background(),
		effectivePageLayoutReaderStub{layouts: map[string][]*models.PageLayout{
			"Lead": {&effective},
		}},
		&release,
		nil,
	)
	if err != nil {
		t.Fatal(err)
	}
	if len(components) != 0 {
		t.Fatalf("unchanged page layout produced override: %#v", components)
	}
}

func TestBuildEffectivePageLayoutComponentsTreatsExactNumericLexemesAsUnchanged(t *testing.T) {
	base := models.PageLayout{
		ObjectAPIName: "Lead", LayoutName: "Lead Layout", IsDefault: true,
		HeaderActions: []models.ActionConfig{{
			Name: "Run", Label: "Run", Type: "Custom",
			Config: map[string]interface{}{
				"scale":   json.Number("12.50"),
				"large":   json.Number("9007199254740993"),
				"precise": json.Number("0.123456789012345678901234567890"),
			},
		}},
	}
	release := verifiedPlatformRelease(t, []platformReleaseComponentFixture{{
		kind: appReleasePageLayoutComponentKind, businessKey: "Lead/default",
		behavior: pageLayoutBehavior(t, base),
	}})
	effective := base
	effective.ID = "curated_Lead_default"
	effective.Source = "curated"
	components, err := buildEffectivePageLayoutComponents(
		context.Background(),
		effectivePageLayoutReaderStub{layouts: map[string][]*models.PageLayout{
			"Lead": {&effective},
		}},
		&release,
		nil,
	)
	if err != nil {
		t.Fatal(err)
	}
	if len(components) != 0 {
		t.Fatalf("exact numeric lexemes produced a false effective override: %#v", components)
	}
}

func TestReadEffectiveMetadataPublishesTenantNativePageLayout(t *testing.T) {
	release := verifiedPlatformRelease(t, nil)
	creditsLayout := &models.PageLayout{
		ID: "curated_Credits__c_default", ObjectAPIName: "Credits__c",
		LayoutName: "Credits Layout", IsDefault: true, Source: "curated",
		Sections: []models.PageSection{{
			ID: "details", Label: "Details", Fields: []string{"Name", "Credit_Type__c"},
		}},
	}
	reader := &completeV3FlowReader{
		markedReader: &markedReader{
			t: t, objects: []*models.ObjectMetadata{validObject("Credits__c")},
		},
		layouts: map[string][]*models.PageLayout{
			"Credits__c": {creditsLayout},
		},
	}
	selection := MetadataSelection{
		OrgID: "org-test", AppInstallationID: "installation-test",
		RuntimeCapabilityIDs: []string{queryv1.CapabilityName},
		CatalogRevision:      7, SchemaGeneration: 8, EngineGeneration: 9,
	}
	result, err := readEffectiveMetadata(
		context.Background(),
		func(ctx context.Context, fn func(context.Context) error) error {
			return fn(context.WithValue(ctx, transactionMarker{}, "one-snapshot"))
		},
		reader, selection, &release, consumerv1.AppMetadataV3CapabilityName,
	)
	if err != nil {
		t.Fatal(err)
	}
	for _, component := range result.components {
		if component.ComponentKind != consumerv1.EffectivePageLayoutComponentKind ||
			component.BusinessKey != "Credits__c/default" {
			continue
		}
		want, err := canonicalPageLayoutModel(creditsLayout)
		if err != nil {
			t.Fatal(err)
		}
		if !bytes.Equal(component.CanonicalBehavior, want) {
			t.Fatalf("effective tenant page layout = %s, want %s", component.CanonicalBehavior, want)
		}
		return
	}
	t.Fatal("effective tenant-native Credits__c/default page layout was not published")
}

func TestBuildEffectivePageLayoutComponentsSkipsTenantObjectWithoutLayout(t *testing.T) {
	release := verifiedPlatformRelease(t, nil)
	components, err := buildEffectivePageLayoutComponents(
		context.Background(),
		effectivePageLayoutReaderStub{layouts: map[string][]*models.PageLayout{}},
		&release,
		[]string{"TenantObject__c"},
	)
	if err != nil {
		t.Fatal(err)
	}
	if len(components) != 0 {
		t.Fatalf("layout-free tenant object produced components: %#v", components)
	}
}

func TestBuildEffectivePageLayoutComponentsRejectsDuplicateTenantObjectIdentity(t *testing.T) {
	release := verifiedPlatformRelease(t, nil)
	_, err := buildEffectivePageLayoutComponents(
		context.Background(),
		effectivePageLayoutReaderStub{layouts: map[string][]*models.PageLayout{}},
		&release,
		[]string{"TenantObject__c", "tenantobject__c"},
	)
	if err == nil || !strings.Contains(err.Error(), "duplicate page-layout identity") {
		t.Fatalf("duplicate tenant object error = %v", err)
	}
}

func TestBuildEffectivePageLayoutComponentsRejectsMalformedTenantDefaultSelection(t *testing.T) {
	release := verifiedPlatformRelease(t, nil)
	for _, test := range []struct {
		name        string
		objectNames []string
		layouts     []*models.PageLayout
		wantError   string
	}{
		{
			name: "malformed object identity", objectNames: []string{"Tenant/Object__c"},
			wantError: "invalid page-layout identity",
		},
		{
			name: "nil layout", objectNames: []string{"TenantObject__c"},
			layouts: []*models.PageLayout{nil}, wantError: "is nil",
		},
		{
			name: "foreign object", objectNames: []string{"TenantObject__c"},
			layouts:   []*models.PageLayout{{ObjectAPIName: "OtherObject__c", IsDefault: true}},
			wantError: "contains foreign object",
		},
		{
			name: "no default", objectNames: []string{"TenantObject__c"},
			layouts:   []*models.PageLayout{{ObjectAPIName: "TenantObject__c"}},
			wantError: "has no default",
		},
		{
			name: "multiple defaults", objectNames: []string{"TenantObject__c"},
			layouts: []*models.PageLayout{
				{ObjectAPIName: "TenantObject__c", IsDefault: true},
				{ObjectAPIName: "TenantObject__c", IsDefault: true},
			},
			wantError: "has multiple defaults",
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			_, err := buildEffectivePageLayoutComponents(
				context.Background(),
				effectivePageLayoutReaderStub{layouts: map[string][]*models.PageLayout{
					"TenantObject__c": test.layouts,
				}},
				&release,
				test.objectNames,
			)
			if err == nil || !strings.Contains(err.Error(), test.wantError) {
				t.Fatalf("tenant default selection error = %v, want %q", err, test.wantError)
			}
		})
	}
}
