package dialectsfcrm

import (
	"context"
	"fmt"
	"strings"

	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/queryspec"
)

// The <Object>History "Created By" column is the change actor
// (_System_AuditLog.ChangedById, projected as CreatedById). The released
// querycodec module owns the neutral history projection and emits only that raw
// id; it is app-blind and cannot know the actor is a User with a Name. The
// Salesforce-shaped CreatedBy relationship is therefore resolved here, in the
// transitional in-process dialect, and injected into the codec response.
//
// This lives in the dialect only because consumers pin the released query codec
// without a replace directive (enforced by dialect-go-seals CI). When the codec
// is next released, this enrichment should move into querycodec.executeHistory,
// which already holds the Executor.Query seam.
const (
	historyActorObject           = "User"
	historyActorRelationshipName = "CreatedBy"
	historyActorIDField          = "CreatedById"
)

// historyActorNames is the shared holder the codec executor uses to publish
// resolved actor names to serveQueryWithCodec after the codec has run.
type historyActorNames struct {
	byID map[string]string
}

// historyActorRelationshipRequested reports whether the SOQL projection asked
// for the CreatedBy relationship (CreatedBy or CreatedBy.<field>). Salesforce
// only nests CreatedBy when a CreatedBy.* field is selected, so a bare
// CreatedById selection pays no extra User lookup.
func historyActorRelationshipRequested(selectFields []string) bool {
	prefix := strings.ToLower(historyActorRelationshipName) + "."
	for _, field := range selectFields {
		trimmed := strings.ToLower(strings.TrimSpace(field))
		if trimmed == strings.ToLower(historyActorRelationshipName) || strings.HasPrefix(trimmed, prefix) {
			return true
		}
	}
	return false
}

// resolveHistoryActorNames resolves the distinct actor ids on a page of history
// rows to their User.Name through the neutral Query seam, AS THE ACTING USER, so
// object/field/record visibility is enforced by the engine: an actor the caller
// cannot read simply does not resolve, and the row keeps its raw id (no
// regression, nothing leaked past FLS). Any lookup error is non-fatal.
func (e legacyQueryCodecExecutor) resolveHistoryActorNames(ctx context.Context, rows []contract.HistoryRecord) map[string]string {
	if e.query == nil || len(rows) == 0 {
		return nil
	}
	ids := make([]string, 0, len(rows))
	seen := make(map[string]struct{}, len(rows))
	for _, row := range rows {
		id := strings.TrimSpace(row.CreatedByID)
		if id == "" {
			continue
		}
		if _, dup := seen[id]; dup {
			continue
		}
		seen[id] = struct{}{}
		ids = append(ids, id)
	}
	if len(ids) == 0 {
		return nil
	}
	result, err := e.query.Query(ctx, queryspec.QuerySpec{
		ObjectAPIName: historyActorObject,
		SelectFields:  []string{"Id", "Name"},
		Filter:        queryspec.FieldPredicate("Id", "IN", ids),
		Limit:         len(ids),
	}, e.user)
	if err != nil || result == nil {
		return nil
	}
	names := make(map[string]string, len(result.Records))
	for _, record := range result.Records {
		id, _ := record["Id"].(string)
		name, _ := record["Name"].(string)
		if id != "" && name != "" {
			names[id] = name
		}
	}
	return names
}

// injectHistoryActorRelationships nests a Salesforce-shaped CreatedBy
// relationship object onto each history record whose actor name resolved.
// Records whose actor did not resolve keep only their raw CreatedById.
func injectHistoryActorRelationships(body any, actors *historyActorNames, version string) {
	if actors == nil || len(actors.byID) == 0 {
		return
	}
	envelope, ok := body.(map[string]any)
	if !ok {
		return
	}
	records, ok := envelope["records"].([]any)
	if !ok {
		return
	}
	for _, item := range records {
		record, ok := item.(map[string]any)
		if !ok {
			continue
		}
		if _, already := record[historyActorRelationshipName]; already {
			continue
		}
		id, _ := record[historyActorIDField].(string)
		id = strings.TrimSpace(id)
		if name := actors.byID[id]; name != "" {
			record[historyActorRelationshipName] = historyActorReference(id, name, version)
		}
	}
}

// historyActorReference builds the Salesforce v63 nested-relationship object for
// a resolved CreatedBy actor, matching the shape a normal SELECT CreatedBy.Name
// traversal returns.
func historyActorReference(id, name, version string) map[string]any {
	return map[string]any{
		"attributes": map[string]any{
			"type": historyActorObject,
			"url":  fmt.Sprintf("/services/data/%s/sobjects/%s/%s", apiVersionOrDefault(version), historyActorObject, id),
		},
		"Id":   id,
		"Name": name,
	}
}
