package packs

import (
	"fmt"
	"strings"

	kernelconnector "github.com/nexuscrm/kernel/pkg/connector"
)

func (b *sourceBackedComponentBuildContext) buildWebhookSecret(component ManifestComponent, payload []byte) error {
	if err := b.requireConnectorPack(ComponentKindWebhookSecret); err != nil {
		return err
	}
	packComponent, err := newSourceBackedWebhookSecretComponent(b.p.Name(), component, payload)
	if err != nil {
		return err
	}
	if suffix, ok := componentBusinessKeySuffix(component.BusinessKey); ok {
		b.webhookSecretBatch.declaredNames = append(b.webhookSecretBatch.declaredNames, suffix)
		b.p.appendRuntimeStartupShallowKey(ComponentKindWebhookSecret, suffix)
	}
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (b *sourceBackedComponentBuildContext) buildConnectorCapability(component ManifestComponent, payload []byte) error {
	packComponent, err := newConnectorCapabilityComponent(b.p.Name(), component, payload)
	if err != nil {
		return err
	}
	compPayload := packComponent.(*connectorCapabilityComponent).payload
	b.p.connectorCapabilityDeclarations = append(b.p.connectorCapabilityDeclarations, compPayload)
	b.p.appendRuntimeStartupShallowKey(ComponentKindConnectorCapability, strings.TrimSpace(compPayload.Namespace)+":"+strings.TrimSpace(compPayload.Kind))
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (b *sourceBackedComponentBuildContext) buildConnectorRateLimit(component ManifestComponent, payload []byte) error {
	if err := b.requireConnectorPack(ComponentKindConnectorRateLimit); err != nil {
		return err
	}
	packComponent, err := newSourceBackedConnectorRateLimitComponent(b.p.Name(), component, payload)
	if err != nil {
		return err
	}
	if suffix, ok := componentBusinessKeySuffix(component.BusinessKey); ok {
		b.connectorRateLimitBatch.declaredEndpointClasses = append(b.connectorRateLimitBatch.declaredEndpointClasses, suffix)
		b.p.appendRuntimeStartupShallowKey(ComponentKindConnectorRateLimit, suffix)
	}
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (b *sourceBackedComponentBuildContext) buildConnectorInboundHandlerConfig(component ManifestComponent, payload []byte) error {
	if err := b.requireConnectorPack(ComponentKindConnectorInboundHandlerConfig); err != nil {
		return err
	}
	packComponent, err := newConnectorInboundHandlerConfigComponent(b.p.Name(), component, payload)
	if err != nil {
		return err
	}
	cc := packComponent.(*connectorInboundHandlerConfigComponent)
	b.p.connectorInboundHandlerConfigDeclarations = append(b.p.connectorInboundHandlerConfigDeclarations, cc.payload)
	b.p.connectorInboundHandlerConfigBatch.declaredCapabilityKinds = append(
		b.p.connectorInboundHandlerConfigBatch.declaredCapabilityKinds,
		cc.payload.CapabilityKind,
	)
	b.p.appendRuntimeStartupShallowKey(ComponentKindConnectorInboundHandlerConfig, cc.payload.CapabilityKind)
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (b *sourceBackedComponentBuildContext) buildConnectorInboundRoute(component ManifestComponent, payload []byte) error {
	if err := b.requireConnectorPack(ComponentKindConnectorInboundRoute); err != nil {
		return err
	}
	packComponent, err := newConnectorInboundRouteComponent(b.p.Name(), component, payload)
	if err != nil {
		return err
	}
	cc := packComponent.(*connectorInboundRouteComponent)
	b.p.connectorInboundRouteBatch.declaredEventKinds = append(
		b.p.connectorInboundRouteBatch.declaredEventKinds, cc.payload.EventKind)
	b.p.appendRuntimeStartupShallowKey(ComponentKindConnectorInboundRoute, cc.payload.EventKind)
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (b *sourceBackedComponentBuildContext) buildConnectorCalloutProfile(component ManifestComponent, payload []byte) error {
	if err := b.requireConnectorPack(ComponentKindConnectorCalloutProfile); err != nil {
		return err
	}
	packComponent, definition, err := newSourceBackedConnectorCalloutProfileComponentWithDefinition(
		b.p.Name(),
		component,
		payload,
		b.p.connectorCalloutProfileCompat.allowLegacyMissingBatchIDResponseField,
	)
	if err != nil {
		return err
	}
	if suffix, ok := componentBusinessKeySuffix(component.BusinessKey); ok {
		b.connectorCalloutProfileBatch.declaredProfileNames = append(b.connectorCalloutProfileBatch.declaredProfileNames, suffix)
		projection, err := marshalConnectorCalloutProfileFinalizationProjection(
			connectorCalloutProfileFinalizationProjection(
				b.p.Name(),
				definition,
			),
		)
		if err != nil {
			return fmt.Errorf(
				"marshal connector callout profile %q finalization projection: %w",
				component.BusinessKey,
				err,
			)
		}
		b.p.appendRuntimeStartupShallowDeclaration(
			ComponentKindConnectorCalloutProfile,
			suffix,
			projection,
		)
	}
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (b *sourceBackedComponentBuildContext) buildConnectorFieldMapping(component ManifestComponent, payload []byte) error {
	packComponent, declared, err := newSourceBackedConnectorFieldMappingComponent(b.p.Name(), component, payload)
	if err != nil {
		return err
	}
	b.p.connectorFieldMappingDeclarations = append(b.p.connectorFieldMappingDeclarations, declared...)
	projection, err := marshalRuntimeStartupShallowProjection(declared)
	if err != nil {
		return fmt.Errorf("marshal connector field mapping %q runtime projection: %w", component.BusinessKey, err)
	}
	b.p.appendRuntimeStartupShallowDeclaration(
		ComponentKindConnectorFieldMapping,
		component.BusinessKey,
		projection,
	)
	for _, row := range declared {
		b.p.appendRuntimeStartupShallowKey(
			ComponentKindFieldMapping,
			fieldMappingStartupShallowBusinessKey(row.SourceObject, row.TargetObject, row.ActionType, row.SourceField, row.Direction),
		)
	}
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (b *sourceBackedComponentBuildContext) buildConnectorPushContract(component ManifestComponent, payload []byte) error {
	packComponent, declared, err := newSourceBackedConnectorPushContractComponent(b.p.Name(), component, payload, b.p.standardFlowPayloads)
	if err != nil {
		return err
	}
	eligibility, err := kernelconnector.ParseConnectorPushRecoverySourceEligibilityFromPayload(payload)
	if err != nil {
		return err
	}
	if eligibility != nil {
		projection, err := marshalRuntimeStartupShallowProjection(eligibility)
		if err != nil {
			return fmt.Errorf("marshal connector push contract %q recovery source eligibility projection: %w", component.BusinessKey, err)
		}
		b.p.appendRuntimeStartupShallowDeclaration(
			componentKindConnectorPushRecoverySourceEligibility,
			component.BusinessKey,
			projection,
		)
	}
	historicalKey, err := kernelconnector.ParseConnectorPushHistoricalReconciliationKeyFromPayload(payload)
	if err != nil {
		return err
	}
	b.p.connectorPushContractDeclarations = append(b.p.connectorPushContractDeclarations, declared)
	if historicalKey != nil {
		b.p.connectorPushHistoricalDecls = append(
			b.p.connectorPushHistoricalDecls,
			sourceBackedConnectorPushHistoricalReconciliationDeclaration{
				Contract: declared,
				Key:      *historicalKey,
			},
		)
	}
	b.p.components = append(b.p.components, packComponent)
	return nil
}

func (p *SourceBackedPack) appendConnectorPushContractRuntimeStartupShallowDeclarations() error {
	for _, declared := range p.connectorPushContractDeclarations {
		businessKey := JoinComponentBusinessKey(
			declared.ConnectorOwnerKey,
			declared.FlowDeveloperName,
			declared.FlowActionName,
			declared.SourceObject,
			declared.DestinationSlotID,
		)
		projection, err := marshalRuntimeStartupShallowProjection(declared)
		if err != nil {
			return fmt.Errorf("marshal connector push contract %q runtime projection: %w", businessKey, err)
		}
		p.appendRuntimeStartupShallowDeclaration(
			ComponentKindConnectorPushContract,
			businessKey,
			projection,
		)
	}
	return nil
}

func (b *sourceBackedComponentBuildContext) requireConnectorPack(kind ComponentKind) error {
	if b.p.Category() == ConnectorPack {
		return nil
	}
	return fmt.Errorf(
		"%s components require category=%s; pack %q declares category=%q",
		kind, ConnectorPack, b.p.Name(), b.p.Category(),
	)
}

func componentBusinessKeySuffix(businessKey string) (string, bool) {
	trimmed := strings.TrimSpace(businessKey)
	if idx := strings.Index(trimmed, "/"); idx >= 0 {
		return trimmed[idx+1:], true
	}
	return "", false
}
