package records

import (
	"context"
	"fmt"
	"sort"
	"strings"

	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/referenceidentity"
)

func resolveReferenceIdentityDispositions(
	ctx context.Context,
	idsByObject map[string][]string,
	fields []string,
) (map[string]map[string]referenceidentity.Disposition, error) {
	authority, bound := referenceidentity.FromContext(ctx)
	if !bound || len(idsByObject) == 0 {
		return nil, nil
	}
	objects := make([]string, 0, len(idsByObject))
	for objectName := range idsByObject {
		objects = append(objects, objectName)
	}
	sort.Strings(objects)
	resolved := make(map[string]map[string]referenceidentity.Disposition, len(objects))
	for _, objectName := range objects {
		ids := uniqueReferenceIdentityIDs(idsByObject[objectName])
		resolution, err := authority.ResolveReferenceIdentities(ctx, objectName, ids, fields)
		if err != nil {
			return nil, appErrors.NewInternalError(
				fmt.Sprintf("failed to resolve authoritative reference identities for %s", objectName), err,
			)
		}
		byID := make(map[string]referenceidentity.Disposition)
		for _, id := range ids {
			identity, ok := resolution.Identities[id]
			if !ok || identity.Disposition == referenceidentity.Unowned {
				continue
			}
			switch identity.Disposition {
			case referenceidentity.Immutable:
				resolvedID, _ := identity.Record["Id"].(string)
				if identity.Record == nil || strings.TrimSpace(resolvedID) != id {
					return nil, appErrors.NewInternalError(
						fmt.Sprintf("reference identity authority returned an invalid immutable record for %s/%s", objectName, id), nil,
					)
				}
				byID[id] = referenceidentity.Immutable
			case referenceidentity.OwnedMissing:
				byID[id] = referenceidentity.OwnedMissing
			default:
				return nil, appErrors.NewInternalError(
					fmt.Sprintf("reference identity authority returned an invalid disposition for %s/%s", objectName, id), nil,
				)
			}
		}
		if len(byID) != 0 {
			resolved[objectName] = byID
		}
	}
	return resolved, nil
}

func uniqueReferenceIdentityIDs(values []string) []string {
	seen := make(map[string]struct{}, len(values))
	result := make([]string, 0, len(values))
	for _, value := range values {
		value = strings.TrimSpace(value)
		if value == "" {
			continue
		}
		if _, exists := seen[value]; exists {
			continue
		}
		seen[value] = struct{}{}
		result = append(result, value)
	}
	sort.Strings(result)
	return result
}
