package dialectsfcrm

import (
	"context"
	"testing"

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

type mapSchemaLookup map[string]*models.ObjectMetadata

func (m mapSchemaLookup) getSchema(_ context.Context, apiName string) *models.ObjectMetadata {
	return m[apiName]
}

func strptr(s string) *string { return &s }

func picklistField(api string, opts models.PicklistOptions) models.FieldMetadata {
	return models.FieldMetadata{APIName: api, Label: api, Type: constants.FieldTypePicklist, Options: opts}
}

func formulaField(api, formula string) models.FieldMetadata {
	return models.FieldMetadata{APIName: api, Label: api, Type: constants.FieldTypeFormula, Formula: strptr(formula), ReturnType: fieldTypePtr(constants.FieldTypeString)}
}

func numFormulaField(api, formula string) models.FieldMetadata {
	return models.FieldMetadata{APIName: api, Label: api, Type: constants.FieldTypeFormula, Formula: strptr(formula), ReturnType: fieldTypePtr(constants.FieldTypeDouble)}
}

func fieldTypePtr(t models.FieldType) *models.FieldType { return &t }

func lookupField(api, relName, target string) models.FieldMetadata {
	return models.FieldMetadata{APIName: api, Label: api, Type: constants.FieldTypeString, RelationshipName: strptr(relName), ReferenceTo: []string{target}}
}

func valuesOf(opts models.PicklistOptions) []string {
	out := make([]string, len(opts))
	for i, o := range opts {
		out[i] = o.Value
	}
	return out
}

func TestResolveFormulaMirrorOptions(t *testing.T) {
	region := models.PicklistOptions{
		{Value: "APAC", Label: "APAC"},
		{Value: "NA", Label: "NA", Tags: []string{"presentation:success"}},
	}
	dealReg := models.PicklistOptions{{Value: "Yes", Label: "Yes"}, {Value: "No", Label: "No"}}

	account := &models.ObjectMetadata{APIName: "Account", Fields: []models.FieldMetadata{
		picklistField("Region__c", region),
	}}
	opportunity := &models.ObjectMetadata{APIName: "Opportunity", Fields: []models.FieldMetadata{
		lookupField("AccountId", "Account", "Account"),
		formulaField("Region__c", "text(Account.Region__c)"),
		picklistField("Deal_Registration__c", dealReg),
		numFormulaField("CD_Rate__c", `CASE(TEXT(CurrencyIsoCode),"USD",1,"JPY",142,7)`),
		formulaField("Governing_Contract_Family__c", `IF(TEXT(Product_Family__c)="TiDB Cloud","Cloud","OP")`),
		picklistField("Status__c", models.PicklistOptions{{Value: "Open", Label: "Open"}}),
		formulaField("Status_Mirror__c", "text(Status__c)"),
	}}
	order := &models.ObjectMetadata{APIName: "Order", Fields: []models.FieldMetadata{
		lookupField("OpportunityId", "Opportunity", "Opportunity"),
		formulaField("Deal_Registration__c", "TEXT(Opportunity.Deal_Registration__c)"),
	}}
	invoice := &models.ObjectMetadata{APIName: "Invoice_Collection__c", Fields: []models.FieldMetadata{
		lookupField("Order__c", "Order__r", "Order"),
		formulaField("Deal_Registration__c", "Order__r.Deal_Registration__c"),
	}}
	// CampaignMember dual-source shape: same source picklist via two relationships.
	lead := &models.ObjectMetadata{APIName: "Lead", Fields: []models.FieldMetadata{picklistField("Region__c", region)}}
	contact := &models.ObjectMetadata{APIName: "Contact", Fields: []models.FieldMetadata{picklistField("Region__c", region)}}
	campaign := &models.ObjectMetadata{APIName: "CampaignMember", Fields: []models.FieldMetadata{
		lookupField("LeadId", "Lead", "Lead"),
		lookupField("ContactId", "Contact", "Contact"),
		formulaField("Region__c", "IF(ISBLANK(ContactId),TEXT(Lead.Region__c),TEXT(Contact.Region__c))"),
		// Contact branch walks a two-hop relationship path (Contact -> Account).
		formulaField("Time_Zone__c", "IF(ISBLANK(ContactId),TEXT(Lead.Region__c),TEXT(Contact.Account.Region__c))"),
	}}
	// Contact carries an Account lookup so Contact.Account.Region__c resolves.
	contact.Fields = append(contact.Fields, lookupField("AccountId", "Account", "Account"))

	schemas := mapSchemaLookup{
		"Account": account, "Opportunity": opportunity, "Order": order,
		"Invoice_Collection__c": invoice, "Lead": lead, "Contact": contact, "CampaignMember": campaign,
	}
	ctx := context.Background()

	cases := []struct {
		name   string
		schema *models.ObjectMetadata
		field  string
		want   []string // nil => expect no mirror
	}{
		{"single cross-object passthrough", opportunity, "Region__c", []string{"APAC", "NA"}},
		{"bare same-object passthrough", opportunity, "Status_Mirror__c", []string{"Open"}},
		{"two-hop chain", invoice, "Deal_Registration__c", []string{"Yes", "No"}},
		{"dual-source agree", campaign, "Region__c", []string{"APAC", "NA"}},
		{"dual-source with two-hop branch", campaign, "Time_Zone__c", []string{"APAC", "NA"}},
		{"numeric CASE formula", opportunity, "CD_Rate__c", nil},
		{"computed category IF", opportunity, "Governing_Contract_Family__c", nil},
		{"real picklist is not a mirror", account, "Region__c", nil},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := resolveFormulaMirrorOptions(ctx, schemas, tc.schema, *fieldByName(tc.schema, tc.field))
			if tc.want == nil {
				if got != nil {
					t.Fatalf("expected no mirror options, got %v", valuesOf(got))
				}
				return
			}
			gotVals := valuesOf(got)
			if len(gotVals) != len(tc.want) {
				t.Fatalf("values = %v, want %v", gotVals, tc.want)
			}
			for i := range tc.want {
				if gotVals[i] != tc.want[i] {
					t.Fatalf("values = %v, want %v", gotVals, tc.want)
				}
			}
		})
	}
}

func TestDualSourceOrderInsensitive(t *testing.T) {
	// Two independently authored picklists with the same values in reversed order
	// must still resolve to a mirror badge.
	fwd := models.PicklistOptions{{Value: "APAC", Label: "APAC"}, {Value: "NA", Label: "NA"}}
	rev := models.PicklistOptions{{Value: "NA", Label: "NA"}, {Value: "APAC", Label: "APAC"}}
	lead := &models.ObjectMetadata{APIName: "Lead", Fields: []models.FieldMetadata{picklistField("Region__c", fwd)}}
	contact := &models.ObjectMetadata{APIName: "Contact", Fields: []models.FieldMetadata{picklistField("Region__c", rev)}}
	cm := &models.ObjectMetadata{APIName: "CampaignMember", Fields: []models.FieldMetadata{
		lookupField("LeadId", "Lead", "Lead"),
		lookupField("ContactId", "Contact", "Contact"),
		formulaField("Region__c", "IF(ISBLANK(ContactId),TEXT(Lead.Region__c),TEXT(Contact.Region__c))"),
	}}
	schemas := mapSchemaLookup{"Lead": lead, "Contact": contact, "CampaignMember": cm}
	got := resolveFormulaMirrorOptions(context.Background(), schemas, cm, *fieldByName(cm, "Region__c"))
	if len(got) != 2 {
		t.Fatalf("expected dual-source with reversed order to resolve, got %v", valuesOf(got))
	}
}

func TestPolymorphicRelationshipExcluded(t *testing.T) {
	region := models.PicklistOptions{{Value: "APAC", Label: "APAC"}}
	// A "wrong" object reachable as ReferenceTo[0] that also has a same-named picklist.
	who := &models.ObjectMetadata{APIName: "Contact", Fields: []models.FieldMetadata{picklistField("Region__c", region)}}
	task := &models.ObjectMetadata{APIName: "Task", Fields: []models.FieldMetadata{
		{APIName: "WhoId", Label: "Who", Type: constants.FieldTypeString, RelationshipName: strptr("Who"), ReferenceTo: []string{"Contact", "Lead"}},
		formulaField("Region__c", "text(Who.Region__c)"),
	}}
	schemas := mapSchemaLookup{"Contact": who, "Task": task}
	if got := resolveFormulaMirrorOptions(context.Background(), schemas, task, *fieldByName(task, "Region__c")); got != nil {
		t.Fatalf("polymorphic relationship must not resolve to a concrete picklist, got %v", valuesOf(got))
	}
}

func TestTransitionalPolymorphicSingleTargetExcluded(t *testing.T) {
	// A transitional polymorphic lookup can advertise a single ReferenceTo while
	// still being polymorphic; the IsPolymorphic flag must exclude it.
	region := models.PicklistOptions{{Value: "APAC", Label: "APAC"}}
	src := &models.ObjectMetadata{APIName: "Contact", Fields: []models.FieldMetadata{picklistField("Region__c", region)}}
	obj := &models.ObjectMetadata{APIName: "Task", Fields: []models.FieldMetadata{
		{APIName: "WhoId", Label: "Who", Type: constants.FieldTypeString, RelationshipName: strptr("Who"), ReferenceTo: []string{"Contact"}, IsPolymorphic: true},
		formulaField("Region__c", "text(Who.Region__c)"),
	}}
	schemas := mapSchemaLookup{"Contact": src, "Task": obj}
	if got := resolveFormulaMirrorOptions(context.Background(), schemas, obj, *fieldByName(obj, "Region__c")); got != nil {
		t.Fatalf("transitional polymorphic (single ReferenceTo, IsPolymorphic) must be excluded, got %v", valuesOf(got))
	}
}

func TestDualSourceTagDivergenceFailsClosed(t *testing.T) {
	tagged := models.PicklistOptions{{Value: "NA", Label: "NA", Tags: []string{"presentation:success"}}}
	untagged := models.PicklistOptions{{Value: "NA", Label: "NA"}}
	lead := &models.ObjectMetadata{APIName: "Lead", Fields: []models.FieldMetadata{picklistField("Region__c", tagged)}}
	contact := &models.ObjectMetadata{APIName: "Contact", Fields: []models.FieldMetadata{picklistField("Region__c", untagged)}}
	cm := &models.ObjectMetadata{APIName: "CampaignMember", Fields: []models.FieldMetadata{
		lookupField("LeadId", "Lead", "Lead"),
		lookupField("ContactId", "Contact", "Contact"),
		formulaField("Region__c", "IF(ISBLANK(ContactId),TEXT(Lead.Region__c),TEXT(Contact.Region__c))"),
	}}
	schemas := mapSchemaLookup{"Lead": lead, "Contact": contact, "CampaignMember": cm}
	if got := resolveFormulaMirrorOptions(context.Background(), schemas, cm, *fieldByName(cm, "Region__c")); got != nil {
		t.Fatalf("dual source with divergent tags must fail closed, got %v", valuesOf(got))
	}
}

func TestMultipicklistSourceExcluded(t *testing.T) {
	multi := models.FieldMetadata{APIName: "Tags__c", Label: "Tags", Type: constants.FieldTypeMultiPicklist,
		Options: models.PicklistOptions{{Value: "A", Label: "A"}, {Value: "B", Label: "B"}}}
	src := &models.ObjectMetadata{APIName: "Account", Fields: []models.FieldMetadata{multi}}
	opp := &models.ObjectMetadata{APIName: "Opportunity", Fields: []models.FieldMetadata{
		lookupField("AccountId", "Account", "Account"),
		formulaField("Tags__c", "text(Account.Tags__c)"),
	}}
	schemas := mapSchemaLookup{"Account": src, "Opportunity": opp}
	if got := resolveFormulaMirrorOptions(context.Background(), schemas, opp, *fieldByName(opp, "Tags__c")); got != nil {
		t.Fatalf("multipicklist source should not produce a single-badge mirror, got %v", valuesOf(got))
	}
}

func TestFormulaMirrorPreservesTags(t *testing.T) {
	region := models.PicklistOptions{
		{Value: "NA", Label: "NA", Tags: []string{"presentation:success"}},
	}
	account := &models.ObjectMetadata{APIName: "Account", Fields: []models.FieldMetadata{picklistField("Region__c", region)}}
	opp := &models.ObjectMetadata{APIName: "Opportunity", Fields: []models.FieldMetadata{
		lookupField("AccountId", "Account", "Account"),
		formulaField("Region__c", "text(Account.Region__c)"),
	}}
	schemas := mapSchemaLookup{"Account": account, "Opportunity": opp}
	got := resolveFormulaMirrorOptions(context.Background(), schemas, opp, *fieldByName(opp, "Region__c"))
	if len(got) != 1 || len(got[0].Tags) != 1 || got[0].Tags[0] != "presentation:success" {
		t.Fatalf("expected presentation:success tag carried through, got %+v", got)
	}
}
