package bootstrap

import (
	"encoding/json"
	"os"
	"sync"
	"testing"

	"github.com/nexuscrm/backend/internal/controlplane/packspec"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/fieldprovenance"
)

// platformStandardFieldsFixture mirrors the list the real bootstrap hook
// will assemble from GetStandardFieldMetadata() + the "Name" fallback.
// Keeping it in one place lets the test stay aligned with production
// wiring.
var platformStandardFieldsFixture = []string{
	constants.FieldID,
	constants.FieldName,
	constants.FieldOwnerID,
	constants.FieldCreatedByID,
	constants.FieldLastModifiedByID,
	constants.FieldCreatedDate,
	constants.FieldLastModifiedDate,
	constants.FieldSystemModstamp,
	constants.FieldIsDeleted,
}

var registerTenantManifestOnce sync.Once

// buildProvenanceForTest constructs a classifier from the real embedded
// manifests. Uses a fresh platform-standard-field list so the test has
// predictable assertions even if the real bootstrap wiring evolves.
func buildProvenanceForTest(t *testing.T) *fieldprovenance.Classifier {
	t.Helper()

	registerTenantManifestOnce.Do(func() {
		raw, err := os.ReadFile("../controlplane/tenants/pingcap/tenant_objects_spec.json")
		if err != nil {
			t.Fatalf("read tenant spec for provenance test: %v", err)
		}
		type obj struct {
			APIName string `json:"apiName"`
			Fields  []struct {
				APIName string `json:"apiName"`
			} `json:"fields"`
		}
		var specs []obj
		if err := json.Unmarshal(raw, &specs); err != nil {
			t.Fatalf("parse tenant spec: %v", err)
		}
		manifest, _ := json.Marshal(specs)
		packspec.RegisterTenantFieldManifest(manifest)
	})

	c, err := BuildFieldProvenanceClassifier(platformStandardFieldsFixture)
	if err != nil {
		t.Fatalf("BuildFieldProvenanceClassifier: %v", err)
	}
	if c == nil {
		t.Fatal("BuildFieldProvenanceClassifier returned nil without error")
	}
	return c
}

func TestFieldProvenance_SystemTableFieldsAreManaged(t *testing.T) {
	c := buildProvenanceForTest(t)

	// _System_Object.Id is declared in system_tables.json → managed.
	if got := c.ClassifySource("_System_Object", "Id"); got != constants.FieldSourceManaged {
		t.Fatalf("_System_Object.Id = %q; want %q",
			got, constants.FieldSourceManaged)
	}
	// _System_Field.ApiName is declared → managed.
	if got := c.ClassifySource("_System_Field", "ApiName"); got != constants.FieldSourceManaged {
		t.Fatalf("_System_Field.ApiName = %q; want %q",
			got, constants.FieldSourceManaged)
	}
}

func TestFieldProvenance_StandardSpecFieldsAreStandardSpec(t *testing.T) {
	c := buildProvenanceForTest(t)

	// Account.Industry is declared in standard_objects_spec.json → standard_spec.
	if got := c.ClassifySource("Account", "Industry"); got != constants.FieldSourceStandardSpec {
		t.Fatalf("Account.Industry = %q; want %q",
			got, constants.FieldSourceStandardSpec)
	}
}

func TestFieldProvenance_TenantSpecOverridesSuffix(t *testing.T) {
	c := buildProvenanceForTest(t)

	// A __c-suffixed field declared in tenant_objects_spec.json must
	// classify as tenant_spec, not custom. This verifies the tenant
	// manifest actually populates the tenantSpec index and beats the
	// __c-suffix fallback. Order carries Product_Family__c and
	// PingCAP_RevRec_Entity__c in the tenant spec as of #1612; Contract
	// carries PingCAP_Contracting_Entity__c.
	tenantOnly := []struct{ obj, field string }{
		{"Order", "Product_Family__c"},
		{"Order", "PingCAP_RevRec_Entity__c"},
		{"Contract", "PingCAP_Contracting_Entity__c"},
		{"Quote", "Product_Family__c"},
	}
	for _, tc := range tenantOnly {
		t.Run(tc.obj+"."+tc.field, func(t *testing.T) {
			got := c.ClassifySource(tc.obj, tc.field)
			if got != constants.FieldSourceTenantSpec {
				t.Fatalf("%s.%s = %q; want %q (tenant manifest must beat __c suffix rule)",
					tc.obj, tc.field, got, constants.FieldSourceTenantSpec)
			}
		})
	}
}

func TestFieldProvenance_QuoteNameIsCompound(t *testing.T) {
	c := buildProvenanceForTest(t)

	// Quote.Name is declared compound in standard_objects_spec.json
	// with composedFrom: ["QuoteNumber"] (issue #1582 PR B landed).
	ok, cf := c.ClassifyCompound("Quote", "Name")
	if !ok {
		t.Fatalf("Quote.Name should be compound; got isCompound=false")
	}
	if len(cf) != 1 || cf[0] != "QuoteNumber" {
		t.Fatalf("Quote.Name composedFrom = %v; want [QuoteNumber]", cf)
	}
}

func TestFieldProvenance_PlatformStandardFieldOnCustomObject(t *testing.T) {
	c := buildProvenanceForTest(t)

	// The #1582 regression scenario: a brand-new admin-created custom
	// object (not in any L1 manifest) has platform-injected standard
	// fields (Name, Id, audit). Each of those MUST classify as managed
	// — otherwise Layer 1d marks Name non-createable and Insert strips
	// it, reproducing the bug.
	customObj := "Regression1582_" + t.Name() + "__c"
	for _, f := range platformStandardFieldsFixture {
		t.Run(f, func(t *testing.T) {
			got := c.ClassifySource(customObj, f)
			if got != constants.FieldSourceManaged {
				t.Fatalf("ClassifySource(%q, %q) = %q; want %q "+
					"(platform standard-field template must win)",
					customObj, f, got, constants.FieldSourceManaged)
			}
		})
	}
}

func TestFieldProvenance_CustomSuffixOnUndeclaredObject(t *testing.T) {
	c := buildProvenanceForTest(t)

	// Admin creates a custom field via CreateField. The field name ends
	// __c, and no manifest declares it → classifier returns custom.
	got := c.ClassifySource("MyObj__c", "My_Field__c")
	if got != constants.FieldSourceCustom {
		t.Fatalf("undeclared __c field = %q; want %q", got, constants.FieldSourceCustom)
	}
}

func TestFieldProvenance_LegacyDiscoveredColumn(t *testing.T) {
	c := buildProvenanceForTest(t)

	// Legacy User shadow columns — physical presence, no declaration,
	// no __c suffix, not in the platform-std list → discovered.
	// This is the "155 declaration-less physical columns on User"
	// population that motivated #1582.
	got := c.ClassifySource("User", "UserPermissionsMarketingUser")
	if got != constants.FieldSourceDiscovered {
		t.Fatalf("legacy User.UserPermissionsMarketingUser = %q; want %q",
			got, constants.FieldSourceDiscovered)
	}
}

func TestInitFieldProvenance_InstallsDefault(t *testing.T) {
	// Save + restore any prior default so this test doesn't leak state.
	prev := fieldprovenance.Default()
	t.Cleanup(func() { fieldprovenance.SetDefault(prev) })

	// Wipe + install via Init.
	fieldprovenance.SetDefault(nil)
	if err := InitFieldProvenance(platformStandardFieldsFixture); err != nil {
		t.Fatalf("InitFieldProvenance: %v", err)
	}

	// Package-level convenience function must now delegate to the
	// installed default and give the production classification.
	if got := fieldprovenance.ClassifySource("Account", "Industry"); got != constants.FieldSourceStandardSpec {
		t.Fatalf("Account.Industry after Init = %q; want %q",
			got, constants.FieldSourceStandardSpec)
	}
}
