package metadata

import (
	"context"
	"log"
	"strings"

	"github.com/nexuscrm/kernel/pkg/utils"
)

// ResolveObjectForKeyPrefix resolves a Salesforce-style record ID prefix to
// the object API name stored in _System_Object.KeyPrefix. Prefix comparison is
// intentionally case-sensitive: Salesforce uses distinct prefixes such as
// "00K" and "00k" for different objects.
func (ms *MetadataService) ResolveObjectForKeyPrefix(ctx context.Context, prefix string) (string, bool) {
	prefix = strings.TrimSpace(prefix)
	if prefix == "" {
		return "", false
	}
	if ms.UsesRequestBoundRuntimeView() {
		view, ok := ms.runtimeView(ctx)
		if !ok || view == nil {
			return "", false
		}
		for _, object := range view.Objects() {
			if object != nil && object.KeyPrefix != nil && strings.TrimSpace(*object.KeyPrefix) == prefix {
				return object.APIName, true
			}
		}
		return "", false
	}
	if err := ms.ensureCoreCacheInitialized(ctx); err != nil {
		log.Printf("Warning: failed to initialize core cache in ResolveObjectForKeyPrefix(%s): %v", prefix, err)
		return "", false
	}

	snap := ms.loadCoreCache()
	if snap == nil {
		return "", false
	}
	objectAPIName, ok := snap.objectByKeyPrefixMap[prefix]
	return objectAPIName, ok
}

// ResolveObjectForRecordID resolves a Salesforce-style record ID to its object
// API name using _System_Object.KeyPrefix from the metadata snapshot. It checks
// the standard 3-character prefix first, then the legacy 2-character custom
// prefix shape kept for older generated IDs.
func (ms *MetadataService) ResolveObjectForRecordID(ctx context.Context, recordID string) (string, bool) {
	recordID = strings.TrimSpace(recordID)
	// UUIDs are opaque record identities even when their first bytes happen to
	// equal a live KeyPrefix. Classify the identifier shape before consulting
	// prefix ownership so generic records retain their physical lookup path.
	if len(recordID) < 3 || utils.IsValidUUID(recordID) {
		return "", false
	}
	if objectAPIName, ok := ms.ResolveObjectForKeyPrefix(ctx, recordID[:3]); ok {
		return objectAPIName, true
	}
	if objectAPIName, ok := ms.ResolveObjectForKeyPrefix(ctx, recordID[:2]); ok {
		return objectAPIName, true
	}
	return "", false
}

// KeyPrefixForObject returns the key prefix stored for an object in
// _System_Object.KeyPrefix. Object API name lookup is case-insensitive, matching
// the rest of MetadataService schema lookup behavior.
func (ms *MetadataService) KeyPrefixForObject(ctx context.Context, objectAPIName string) (string, bool) {
	objectKey := strings.ToLower(strings.TrimSpace(objectAPIName))
	if objectKey == "" {
		return "", false
	}
	if ms.UsesRequestBoundRuntimeView() {
		view, ok := ms.runtimeView(ctx)
		if !ok || view == nil {
			return "", false
		}
		object, ok := view.Object(objectKey)
		if !ok || object == nil || object.KeyPrefix == nil {
			return "", false
		}
		prefix := strings.TrimSpace(*object.KeyPrefix)
		return prefix, prefix != ""
	}
	if err := ms.ensureCoreCacheInitialized(ctx); err != nil {
		log.Printf("Warning: failed to initialize core cache in KeyPrefixForObject(%s): %v", objectAPIName, err)
		return "", false
	}

	snap := ms.loadCoreCache()
	if snap == nil {
		return "", false
	}
	keyPrefix, ok := snap.keyPrefixByObjectMap[objectKey]
	return keyPrefix, ok
}

// CanonicalObjectAPIName resolves object API name casing from MetadataService's
// schema snapshot. It does not apply aliases; aliases are not persisted metadata
// today and should not be reintroduced as a parallel runtime authority.
func (ms *MetadataService) CanonicalObjectAPIName(ctx context.Context, candidate string) (string, bool) {
	objectKey := strings.ToLower(strings.TrimSpace(candidate))
	if objectKey == "" {
		return "", false
	}
	if ms.UsesRequestBoundRuntimeView() {
		view, ok := ms.runtimeView(ctx)
		if !ok || view == nil {
			return "", false
		}
		object, ok := view.Object(objectKey)
		if !ok || object == nil {
			return "", false
		}
		return object.APIName, true
	}
	if err := ms.ensureCoreCacheInitialized(ctx); err != nil {
		log.Printf("Warning: failed to initialize core cache in CanonicalObjectAPIName(%s): %v", candidate, err)
		return "", false
	}

	snap := ms.loadCoreCache()
	if snap == nil {
		return "", false
	}
	schema, ok := snap.schemaMap[objectKey]
	if !ok || schema == nil {
		return "", false
	}
	return schema.APIName, true
}
