package catalogsnapshot

import (
	"encoding/json"
	"strings"

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

func flowDefinitionToModel(value runtimeFlowDefinitionRecord) *models.FlowDefinition {
	return &models.FlowDefinition{
		Id: value.ID, DeveloperName: value.DeveloperName, ProcessType: value.ProcessType,
		TriggerType: value.TriggerType, TriggerObjectOrEvent: value.TriggerObjectOrEvent,
		ActiveVersionId: value.ActiveVersionID, IsDeleted: value.IsDeleted,
		IsCustomized: value.IsCustomized, OwnerPack: value.OwnerPack,
	}
}

func flowVersionToModel(value runtimeFlowVersionRecord) *models.FlowVersion {
	version := &models.FlowVersion{
		Id: value.ID, DefinitionId: value.DefinitionID, VersionNumber: value.VersionNumber,
		Status: value.Status, ProcessType: value.ProcessType, Source: value.Source,
		DefinitionIsCustomized: value.DefinitionIsCustomized, DefinitionOwnerPack: value.DefinitionOwnerPack,
		DefinitionRuntimeLoaded: true, IsDeleted: value.IsDeleted,
		MetadataRaw: append(json.RawMessage(nil), value.Metadata...), MetadataJSON: string(value.Metadata),
	}
	version.HydrateMetadata()
	return version
}

func (v *RuntimeMetadataView) ValidationRulesForObject(objectAPIName string) []*models.ValidationRule {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]*models.ValidationRule, 0)
	for i := range v.doc.Mutation.ValidationRules {
		value := &v.doc.Mutation.ValidationRules[i]
		if strings.EqualFold(value.ObjectAPIName, objectAPIName) {
			copy, _ := cloneJSON(*value)
			out = append(out, &copy)
		}
	}
	return out
}

func (v *RuntimeMetadataView) RecordTypesForObject(objectAPIName string) []*models.RecordType {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]*models.RecordType, 0)
	for i := range v.doc.Mutation.RecordTypes {
		value := v.doc.Mutation.RecordTypes[i]
		if strings.EqualFold(value.ObjectAPIName, objectAPIName) {
			copy := value
			copy.Description = clonePointer(value.Description)
			copy.BusinessProcessID = clonePointer(value.BusinessProcessID)
			out = append(out, &copy)
		}
	}
	return out
}

func (v *RuntimeMetadataView) TriggerSubscriptions(keyPrefix, eventType, recordTriggerType string) []TriggerSubscription {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]TriggerSubscription, 0)
	for _, value := range v.doc.Mutation.TriggerSubscriptions {
		if value.KeyPrefix == keyPrefix && value.EventType == eventType && value.RecordTriggerType == recordTriggerType && value.IsActive {
			out = append(out, value)
		}
	}
	return out
}

func (v *RuntimeMetadataView) HasTriggerSubscription(keyPrefix string) bool {
	if v == nil || v.doc.Mutation == nil {
		return false
	}
	for _, value := range v.doc.Mutation.TriggerSubscriptions {
		if value.KeyPrefix == keyPrefix && value.IsActive {
			return true
		}
	}
	return false
}

func (v *RuntimeMetadataView) FlowDefinition(id string) (*models.FlowDefinition, bool) {
	if v == nil || v.doc.Mutation == nil {
		return nil, false
	}
	for _, value := range v.doc.Mutation.FlowDefinitions {
		if value.ID == id {
			return flowDefinitionToModel(value), true
		}
	}
	return nil, false
}

// ScheduledFlowDefinitions enumerates only the scheduled roots published in
// this exact immutable Runtime view. It never consults repository/global
// metadata, so independent Workers discover work from the same revision they
// later persist on each occurrence.
func (v *RuntimeMetadataView) ScheduledFlowDefinitions() []*models.FlowDefinition {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]*models.FlowDefinition, 0)
	for _, value := range v.doc.Mutation.FlowDefinitions {
		if value.IsDeleted || !strings.EqualFold(value.TriggerType, "Scheduled") {
			continue
		}
		out = append(out, flowDefinitionToModel(value))
	}
	return out
}

func (v *RuntimeMetadataView) FlowVersions(ids []string) map[string]*models.FlowVersion {
	if v == nil || v.doc.Mutation == nil || len(ids) == 0 {
		return nil
	}
	wanted := make(map[string]struct{}, len(ids))
	for _, id := range ids {
		wanted[id] = struct{}{}
	}
	out := make(map[string]*models.FlowVersion, len(ids))
	for _, value := range v.doc.Mutation.FlowVersions {
		if _, ok := wanted[value.ID]; ok {
			out[value.ID] = flowVersionToModel(value)
		}
	}
	return out
}

func (v *RuntimeMetadataView) ActiveFlowVersion(apiName string) (*models.FlowVersion, bool) {
	if v == nil || v.doc.Mutation == nil {
		return nil, false
	}
	for _, definition := range v.doc.Mutation.FlowDefinitions {
		if !strings.EqualFold(definition.DeveloperName, strings.TrimSpace(apiName)) || definition.IsDeleted {
			continue
		}
		for _, version := range v.doc.Mutation.FlowVersions {
			if version.ID == definition.ActiveVersionID && version.DefinitionID == definition.ID && !version.IsDeleted {
				return flowVersionToModel(version), true
			}
		}
		return nil, false
	}
	return nil, false
}

func (v *RuntimeMetadataView) FlowVersion(id string) (*models.FlowVersion, bool) {
	if v == nil || v.doc.Mutation == nil {
		return nil, false
	}
	for _, version := range v.doc.Mutation.FlowVersions {
		if version.ID == id && !version.IsDeleted {
			return flowVersionToModel(version), true
		}
	}
	return nil, false
}

func (v *RuntimeMetadataView) AutoNumberRulesForObject(objectAPIName string) []AutoNumberRule {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]AutoNumberRule, 0)
	for _, value := range v.doc.Mutation.AutoNumberRules {
		if strings.EqualFold(value.ObjectAPIName, objectAPIName) {
			copy, _ := cloneJSON(value)
			out = append(out, copy)
		}
	}
	return out
}

func (v *RuntimeMetadataView) AutoNumberRule(fieldID string) (*AutoNumberRule, bool) {
	if v == nil || v.doc.Mutation == nil {
		return nil, false
	}
	for _, value := range v.doc.Mutation.AutoNumberRules {
		if value.FieldID == fieldID {
			copy, _ := cloneJSON(value)
			return &copy, true
		}
	}
	return nil, false
}

// FieldMappingRulesForTarget returns only the legacy record-mutation mapping
// closure. Connector execution authority is stored in a distinct typed
// closure and cannot enter this read path.
func (v *RuntimeMetadataView) FieldMappingRulesForTarget(objectAPIName string) []FieldMappingRule {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]FieldMappingRule, 0)
	for _, value := range v.doc.Mutation.FieldMappingRules {
		if strings.EqualFold(value.TargetObject, objectAPIName) {
			out = append(out, value)
		}
	}
	return out
}

// InboundConnectorFieldMappingRulesForTarget returns only the normalized,
// revision-bound connector mapping authority for one target object. Runtime
// views are validated before construction, so every returned row has an
// explicit connector Source and Direction=inbound.
func (v *RuntimeMetadataView) InboundConnectorFieldMappingRulesForTarget(objectAPIName string) []ConnectorInboundFieldMappingRule {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]ConnectorInboundFieldMappingRule, 0)
	for _, value := range v.doc.Mutation.ConnectorInboundFieldMappingRules {
		if strings.EqualFold(value.TargetObject, objectAPIName) {
			out = append(out, value)
		}
	}
	return out
}

func (v *RuntimeMetadataView) ConnectorInboundAuthorshipBindings(connectorOwnerKey string) []ConnectorInboundAuthorshipBinding {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]ConnectorInboundAuthorshipBinding, 0)
	for _, value := range v.doc.Mutation.ConnectorInboundAuthorship {
		if value.ConnectorOwnerKey == connectorOwnerKey {
			out = append(out, value)
		}
	}
	return out
}

// ConnectorInboundFinalizationAuthorities returns detached, immutable
// finalization authority for the exact opaque connector owner selected by this
// Runtime view. The projection contains profile and credential identities only;
// it never contains credential values or provider-callout authority.
func (v *RuntimeMetadataView) ConnectorInboundFinalizationAuthorities(connectorOwnerKey string) []ConnectorInboundFinalizationAuthority {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]ConnectorInboundFinalizationAuthority, 0)
	for _, value := range v.doc.Mutation.ConnectorInboundFinalization {
		if value.Contract.ConnectorOwnerKey != connectorOwnerKey {
			continue
		}
		copy, err := cloneJSON(value)
		if err != nil {
			return nil
		}
		out = append(out, copy)
	}
	return out
}

// CreateFromSourceGuardsForTarget returns a detached, deterministically ordered
// copy of the guard facts compiled for one target object.
func (v *RuntimeMetadataView) CreateFromSourceGuardsForTarget(objectAPIName string) []CreateFromSourceGuardRule {
	if v == nil || v.doc.Mutation == nil {
		return nil
	}
	out := make([]CreateFromSourceGuardRule, 0)
	for _, value := range v.doc.Mutation.CreateSourceGuards {
		if strings.EqualFold(value.TargetObject, objectAPIName) {
			out = append(out, value)
		}
	}
	return out
}

// AllowsPublicOAuthScope evaluates only an immutable decision whose owning
// metadata object is already present in this capability-scoped view.
// Owner-bound scopes are intentionally excluded from the ownerless surface.
func (v *RuntimeMetadataView) AllowsPublicOAuthScope(scope string) bool {
	if v == nil || v.doc.Mutation == nil {
		return false
	}
	want := strings.ToLower(strings.TrimSpace(scope))
	if want == "" {
		return false
	}
	for _, value := range v.doc.Mutation.OAuthPublicScopes {
		if value.Name == want && value.OwnerPack == "" {
			return true
		}
	}
	return false
}

// AllowsPublicOAuthScopeForPack serves neutral source-backed policy consumers.
func (v *RuntimeMetadataView) AllowsPublicOAuthScopeForPack(scope, packName string) bool {
	if v == nil || v.doc.Mutation == nil {
		return false
	}
	wantScope := strings.ToLower(strings.TrimSpace(scope))
	wantPack := strings.TrimSpace(packName)
	if wantScope == "" || wantPack == "" {
		return false
	}
	for _, value := range v.doc.Mutation.OAuthPublicScopes {
		if value.Name == wantScope && value.OwnerPack == wantPack {
			return true
		}
	}
	return false
}
