package sfcrmappservice

import (
	"context"
	querycodec "github.com/pingcap-inc/nexuscrm-app-packs/sfcrm/querycodec"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	"testing"
	"time"
)

func TestQueryCalendarUsesTrustedUserAndRetainsPageAnchor(t *testing.T) {
	view := searchTestView(t, []searchObjectSpec{{APIName: "Item", Label: "Item", Fields: []searchFieldSpec{{APIName: "CreatedDate", Label: "Created", Type: "datetime"}}}})
	calls := 0
	d := &runtimeQueryDates{metadata: func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
		return view, nil
	}, query: func(ctx context.Context, bearer string, req semantic.Request) (semantic.Response, error) {
		calls++
		if bearer != "token" || req.ObjectID != "User" || req.Filter.Kind != semantic.PredicateComparison || req.Filter.Operator != semantic.ComparisonEqual || req.Filter.Left.Kind != semantic.OperandField || req.Filter.Left.Field != "Id" || req.Filter.Right.Kind != semantic.OperandTrustedContext || req.Filter.Right.TrustedContext != semantic.TrustedContextPrincipalID || req.MinCatalogRevision == nil || *req.MinCatalogRevision != view.Receipts.CatalogRevision {
			t.Fatalf("calendar read is not bound to trusted user and metadata: %#v", req)
		}
		zone := "America/Los_Angeles"
		receipts := view.Receipts
		receipts.CapabilityVersion = semantic.CapabilityName
		return semantic.Response{Done: true, TotalSize: 1, Receipts: receipts, Records: []semantic.Record{{ObjectID: "User", Fields: []semantic.Field{{Name: "TimeZoneSidKey", Value: semantic.Value{Kind: semantic.ValueString, String: &zone}}}}}}, nil
	}}
	q, err := querycodec.ParseSOQL("SELECT Id FROM Item WHERE CreatedDate = TODAY")
	if err != nil {
		t.Fatal(err)
	}
	ctx, err := d.resolve(queryDateTestContext(t), q, nil)
	if err != nil {
		t.Fatal(err)
	}
	if q.DateAnchor == nil || q.DateAnchor.TimeZone != "America/Los_Angeles" {
		t.Fatalf("anchor=%#v", q.DateAnchor)
	}
	if _, ok := ctx.Value(queryDateMetadataKey{}).(catalogsemantic.AppMetadataViewV3); !ok {
		t.Fatal("metadata authority missing from execution context")
	}
	anchor := &querycodec.DateAnchor{Instant: time.Date(2026, 9, 8, 0, 30, 0, 0, time.UTC), TimeZone: "America/Los_Angeles"}
	page, _ := querycodec.ParseSOQL("SELECT Id FROM Item WHERE CreatedDate = TODAY")
	if _, err = d.resolve(ctx, page, anchor); err != nil {
		t.Fatal(err)
	}
	if calls != 1 || !page.DateAnchor.Instant.Equal(anchor.Instant) {
		t.Fatalf("calendar reread or anchor changed: calls=%d anchor=%#v", calls, page.DateAnchor)
	}
}

func TestDateMetadataResolvesFormulaRelationshipAndChildWithoutGuessing(t *testing.T) {
	specs := map[string]searchObjectSpec{"item": {APIName: "Item", Fields: []searchFieldSpec{{APIName: "Due", Type: "formula", ReturnType: "date"}, {APIName: "ParentId", Type: "reference", ReferenceTo: []string{"Parent"}, RelationshipName: "Parent", ChildRelationshipName: "Items"}}}, "parent": {APIName: "Parent", Fields: []searchFieldSpec{{APIName: "CreatedDate", Type: "datetime"}}}}
	for field, want := range map[string]string{"Due": "date", "parent.CreatedDate": "datetime"} {
		got, err := dateFieldType(specs, "Item", field)
		if err != nil || got != want {
			t.Fatalf("%s=%s,%v", field, got, err)
		}
	}
	if got, err := dateChildObject(specs, "Parent", "Items"); err != nil || got != "Item" {
		t.Fatalf("child=%s,%v", got, err)
	}
	for _, field := range []string{"Missing", "Parent.Missing", "Missing.CreatedDate", "Parent.CreatedDate.More"} {
		if _, err := dateFieldType(specs, "Item", field); err == nil {
			t.Fatalf("unknown path accepted: %s", field)
		}
	}
}

func TestQueryCalendarRejectsMissingOrInvalidUserTimezone(t *testing.T) {
	view := searchTestView(t, []searchObjectSpec{{APIName: "Item", Label: "Item", Fields: []searchFieldSpec{{APIName: "CreatedDate", Label: "Created", Type: "datetime"}}}})
	for _, zone := range []string{"", "Not/A_Timezone", "Local"} {
		d := &runtimeQueryDates{metadata: func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			return view, nil
		}, query: func(context.Context, string, semantic.Request) (semantic.Response, error) {
			receipts := view.Receipts
			receipts.CapabilityVersion = semantic.CapabilityName
			return semantic.Response{Done: true, TotalSize: 1, Receipts: receipts, Records: []semantic.Record{{ObjectID: "User", Fields: []semantic.Field{{Name: "TimeZoneSidKey", Value: semantic.Value{Kind: semantic.ValueString, String: &zone}}}}}}, nil
		}}
		q, _ := querycodec.ParseSOQL("SELECT Id FROM Item WHERE CreatedDate = TODAY")
		if _, err := d.resolve(queryDateTestContext(t), q, nil); err == nil {
			t.Fatalf("timezone %q silently accepted", zone)
		}
	}
}

func TestDateBoundQueryRejectsDifferentMetadataAuthority(t *testing.T) {
	view := searchTestView(t, []searchObjectSpec{{APIName: "Item", Label: "Item", Fields: []searchFieldSpec{{APIName: "CreatedDate", Label: "Created", Type: "datetime"}}}})
	adapter := &RuntimeQueryAdapter{dates: &runtimeQueryDates{}, query: func(_ context.Context, _ string, req semantic.Request) (semantic.Response, error) {
		if req.MinCatalogRevision == nil || *req.MinCatalogRevision != view.Receipts.CatalogRevision {
			t.Fatal("query did not require calendar metadata revision")
		}
		receipts := view.Receipts
		receipts.CapabilityVersion = semantic.CapabilityName
		receipts.CatalogRevision++
		return semantic.Response{Done: true, Receipts: receipts}, nil
	}}
	ctx := context.WithValue(queryDateTestContext(t), queryDateMetadataKey{}, view)
	if _, err := adapter.queryRecords(ctx, querycodec.QueryRequest{ObjectAPIName: "Item", SelectFields: []string{"Id"}}, 0); err == nil {
		t.Fatal("result from a different metadata revision accepted")
	}
}

func queryDateTestContext(t *testing.T) context.Context {
	t.Helper()
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	t.Cleanup(cancel)
	return withQueryBearer(ctx, "token")
}
