package persistence

import (
	"strings"
	"testing"

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

func TestPrepareCatalogObjectIdentityCandidatesEnforcesExactV1Boundary(t *testing.T) {
	valid64 := "A" + strings.Repeat("b", 63)
	if len(valid64) != 64 {
		t.Fatalf("valid fixture length = %d, want 64", len(valid64))
	}

	tests := []struct {
		name        string
		apiName     string
		wantKey     string
		wantErr     bool
		wantErrPart string
	}{
		{name: "one_byte", apiName: "_", wantKey: "_"},
		{name: "64_bytes", apiName: valid64, wantKey: strings.ToLower(valid64)},
		{name: "empty", apiName: "", wantErr: true, wantErrPart: "1..64"},
		{name: "65_bytes", apiName: "A" + strings.Repeat("b", 64), wantErr: true, wantErrPart: "1..64"},
		{name: "non_ASCII", apiName: "Café__c", wantErr: true, wantErrPart: "[A-Za-z_]"},
		{name: "padded", apiName: " Widget__c ", wantErr: true, wantErrPart: "[A-Za-z_]"},
		{name: "leading_digit", apiName: "1Widget__c", wantErr: true, wantErrPart: "[A-Za-z_]"},
		{name: "punctuation", apiName: "Widget-Item__c", wantErr: true, wantErrPart: "[A-Za-z_]"},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			candidates, err := prepareCatalogObjectIdentityCandidates([]*models.ObjectMetadata{{
				ID:      "obj-widget",
				APIName: test.apiName,
				Label:   "Widget",
			}})
			if test.wantErr {
				if err == nil {
					t.Fatal("prepareCatalogObjectIdentityCandidates() error = nil, want validation error")
				}
				if test.wantErrPart != "" && !strings.Contains(err.Error(), test.wantErrPart) {
					t.Fatalf("error = %q, want substring %q", err, test.wantErrPart)
				}
				return
			}
			if err != nil {
				t.Fatalf("prepareCatalogObjectIdentityCandidates(): %v", err)
			}
			if len(candidates) != 1 {
				t.Fatalf("candidate count = %d, want 1", len(candidates))
			}
			got := candidates[0]
			if got.ID != "obj-widget" ||
				got.APIName != test.apiName ||
				got.NormalizedAPIName != test.wantKey {
				t.Fatalf("candidate = %#v, want ID=obj-widget APIName=%q NormalizedAPIName=%q", got, test.apiName, test.wantKey)
			}
		})
	}
}

func TestPrepareCatalogObjectIdentityCandidatesRejectsInCommandMappingCollisions(t *testing.T) {
	tests := []struct {
		name    string
		objects []*models.ObjectMetadata
	}{
		{
			name: "same_ID_different_name",
			objects: []*models.ObjectMetadata{
				{ID: "obj-shared", APIName: "Widget__c"},
				{ID: "obj-shared", APIName: "Gadget__c"},
			},
		},
		{
			name: "same_normalized_name_different_ID",
			objects: []*models.ObjectMetadata{
				{ID: "obj-upper", APIName: "Widget__c"},
				{ID: "obj-lower", APIName: "widget__c"},
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			if _, err := prepareCatalogObjectIdentityCandidates(test.objects); err == nil {
				t.Fatal("prepareCatalogObjectIdentityCandidates() error = nil, want immutable mapping collision")
			}
		})
	}
}
