package catalogsnapshot

import (
	"context"
	"fmt"
	"strings"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/referenceidentity"
)

func referenceIdentityDisplayName(values ...string) string {
	for _, value := range values {
		if value = strings.TrimSpace(value); value != "" {
			return value
		}
	}
	return ""
}

func validateRuntimeReferenceIdentities(doc runtimeViewDocument) error {
	if len(doc.ReferenceIdentities) == 0 {
		if doc.Schema == RuntimeMetadataViewSchemaV20 {
			return fmt.Errorf("v20 record-query Runtime metadata view contains no reference identities")
		}
		return nil
	}
	if doc.Schema != RuntimeMetadataViewSchemaV20 || doc.Profile != ProjectionRecordQueryV1 {
		return fmt.Errorf("reference identities require the v20 record-query profile")
	}
	objects := make(map[string]struct{}, len(doc.Objects))
	for _, object := range doc.Objects {
		objects[foldIdentity(object.APIName)] = struct{}{}
	}
	for _, required := range []string{constants.TableProcessDefinition, constants.TableProcessNode} {
		if _, ok := objects[foldIdentity(required)]; !ok {
			return fmt.Errorf("reference identities require Runtime object %q", required)
		}
	}
	definitions := make(map[string]struct{})
	nodes := make([]runtimeReferenceIdentityRecord, 0)
	seen := make(map[string]struct{}, len(doc.ReferenceIdentities))
	for index, record := range doc.ReferenceIdentities {
		if err := requireApprovalIdentity(fmt.Sprintf("referenceIdentities[%d].id", index), record.ID); err != nil {
			return err
		}
		if strings.TrimSpace(record.Name) == "" || strings.TrimSpace(record.DeveloperName) == "" {
			return fmt.Errorf("reference identity %q has incomplete display identity", record.ID)
		}
		key := foldIdentity(record.ObjectAPIName) + "\x00" + record.ID
		if _, duplicate := seen[key]; duplicate {
			return fmt.Errorf("reference identity %q/%q is duplicated", record.ObjectAPIName, record.ID)
		}
		seen[key] = struct{}{}
		switch record.ObjectAPIName {
		case constants.TableProcessDefinition:
			if record.ProcessDefinitionID != "" || record.StepOrder != 0 {
				return fmt.Errorf("ProcessDefinition reference identity %q contains node fields", record.ID)
			}
			definitions[record.ID] = struct{}{}
		case constants.TableProcessNode:
			if strings.TrimSpace(record.ProcessDefinitionID) == "" || record.StepOrder <= 0 {
				return fmt.Errorf("ProcessNode reference identity %q has incomplete parent/order", record.ID)
			}
			nodes = append(nodes, record)
		default:
			return fmt.Errorf("reference identity object %q is unsupported", record.ObjectAPIName)
		}
	}
	for _, node := range nodes {
		if _, ok := definitions[node.ProcessDefinitionID]; !ok {
			return fmt.Errorf("ProcessNode reference identity %q has no projected definition parent", node.ID)
		}
	}
	return nil
}

// ResolveReferenceIdentities implements the generic request-bound logical
// reference authority for a record-query view. Other profiles and objects are
// unowned and retain the physical repository behavior.
func (v *RuntimeMetadataView) ResolveReferenceIdentities(
	_ context.Context,
	objectName string,
	ids []string,
	_ []string,
) (referenceidentity.Resolution, error) {
	if v == nil || v.doc.Profile != ProjectionRecordQueryV1 || v.doc.Schema != RuntimeMetadataViewSchemaV20 {
		return referenceidentity.Resolution{}, nil
	}
	byID, owned := v.referenceIdentitiesByObject[foldIdentity(objectName)]
	if !owned {
		return referenceidentity.Resolution{}, nil
	}
	result := referenceidentity.Resolution{
		Identities: make(map[string]referenceidentity.IdentityResolution, len(ids)),
	}
	for _, id := range ids {
		index, exists := byID[id]
		if !exists {
			// Catalog-native node IDs have a collision-free owned namespace, so a
			// node from another revision is distinguishable from a legacy row and
			// must fail closed. Authored definition IDs have no such namespace;
			// absent IDs remain unowned so legacy physical definitions can coexist.
			if strings.EqualFold(objectName, constants.TableProcessNode) && strings.HasPrefix(id, "catalog-approval-node-") {
				result.Identities[id] = referenceidentity.IdentityResolution{Disposition: referenceidentity.OwnedMissing}
			}
			continue
		}
		record := v.doc.ReferenceIdentities[index]
		row := models.SObject{
			constants.FieldID: record.ID, constants.FieldName: record.Name,
			"DeveloperName": record.DeveloperName,
		}
		if strings.EqualFold(record.ObjectAPIName, constants.TableProcessNode) {
			row["ProcessDefinitionId"] = record.ProcessDefinitionID
			row["StepOrder"] = record.StepOrder
		}
		result.Identities[id] = referenceidentity.IdentityResolution{
			Disposition: referenceidentity.Immutable, Record: row,
		}
	}
	return result, nil
}
