package dialectsfcrm

import (
	"context"
	"strings"
	"time"

	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
	querycodec "github.com/pingcap-inc/nexuscrm-app-packs/sfcrm/querycodec"
)

func (e legacyQueryCodecExecutor) resolveDates(ctx context.Context, q *querycodec.QueryRequest, anchor *querycodec.DateAnchor) (context.Context, error) {
	unavailable := func() error {
		return &querycodec.Failure{Category: querycodec.FailureUnavailable, Message: "Query calendar context is unavailable"}
	}
	if e.metadata == nil || e.query == nil || e.user == nil || e.user.ID == "" {
		return ctx, unavailable()
	}
	calendar := querycodec.DateContext{Now: time.Now(), FieldType: e.dateFieldType, ChildObject: e.dateChildObject}
	if anchor == nil {
		result, err := e.query.Query(ctx, queryspec.QuerySpec{ObjectAPIName: "User", SelectFields: []string{"TimeZoneSidKey"}, Filter: queryspec.FieldPredicate("Id", "=", e.user.ID), Limit: 1}, e.user)
		if err != nil {
			return ctx, legacyQueryFailure(e.version, err)
		}
		if result == nil || len(result.Records) != 1 {
			return ctx, unavailable()
		}
		zone, _ := result.Records[0]["TimeZoneSidKey"].(string)
		if strings.TrimSpace(zone) == "" {
			return ctx, unavailable()
		}
		calendar.Location, err = time.LoadLocation(zone)
		if err != nil {
			return ctx, unavailable()
		}
	}
	return ctx, querycodec.ResolveRelativeDates(ctx, q, calendar, anchor)
}

func (e legacyQueryCodecExecutor) dateFieldType(ctx context.Context, object, path string) (string, error) {
	schema, err := e.metadata.Describe(ctx, object)
	if err != nil {
		return "", legacyQueryFailure(e.version, err)
	}
	invalid := func() (string, error) {
		return "", &querycodec.Failure{Category: querycodec.FailureInvalidField, Message: "Invalid field in date predicate"}
	}
	if schema == nil {
		return invalid()
	}
	parts := strings.Split(path, ".")
	if len(parts) == 2 {
		var target string
		for _, field := range schema.Fields {
			if strings.EqualFold(field.APIName, parts[0]) || (field.RelationshipName != nil && strings.EqualFold(*field.RelationshipName, parts[0])) {
				if target != "" || field.IsPolymorphic || len(field.ReferenceTo) != 1 {
					return invalid()
				}
				target = field.ReferenceTo[0]
			}
		}
		if target == "" {
			return invalid()
		}
		return e.dateFieldType(ctx, target, parts[1])
	}
	if len(parts) != 1 {
		return invalid()
	}
	field := models.FindField(schema, path)
	if field == nil {
		return invalid()
	}
	if field.ReturnType != nil {
		return string(*field.ReturnType), nil
	}
	return string(field.Type), nil
}

func (e legacyQueryCodecExecutor) dateChildObject(ctx context.Context, parent, relationship string) (string, error) {
	objects, err := e.metadata.ListObjects(ctx)
	if err != nil {
		return "", legacyQueryFailure(e.version, err)
	}
	var target string
	for _, object := range objects {
		for _, field := range object.Fields {
			if field.ChildRelationshipName == nil || !strings.EqualFold(*field.ChildRelationshipName, relationship) {
				continue
			}
			for _, ref := range field.ReferenceTo {
				if strings.EqualFold(ref, parent) {
					if target != "" {
						return "", &querycodec.Failure{Category: querycodec.FailureInvalidField, Message: "Ambiguous child relationship in date predicate"}
					}
					target = object.APIName
				}
			}
		}
	}
	if target == "" {
		return "", &querycodec.Failure{Category: querycodec.FailureInvalidField, Message: "Invalid child relationship in date predicate"}
	}
	return target, nil
}
