package runtimeapi

import (
	"context"
	"errors"
	"fmt"

	"github.com/nexuscrm/metadata/catalogsnapshot"
	commandsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commandv1"
	outboundsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/outboundintentv1"
	pagelayoutsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/pagelayoutresolutionv1"
)

// ServingConsumerSelection attests the original deployment snapshot and the
// consumer workload. Current content comes from the same active Catalog source
// as the handler-backed serving set; consumers never acquire a HandlerBinding.
type ServingConsumerSelection struct {
	OrgID, AppInstallationID, WorkloadID, CapabilityID string
	DeploymentSnapshotDigest                           catalogsnapshot.Digest
}

func servingConsumerProfile(capability string) catalogsnapshot.ProjectionProfile {
	switch capability {
	case commandsemantic.CapabilityName:
		return catalogsnapshot.ProjectionCommandAcceptanceV1
	case outboundsemantic.CapabilityName:
		return catalogsnapshot.ProjectionRecordMutationV1
	case pagelayoutsemantic.CapabilityName:
		return catalogsnapshot.ProjectionPageLayoutResolutionV1
	default:
		return ""
	}
}

func validateServingConsumers(selections []ServingConsumerSelection) error {
	seen := make(map[string]bool, len(selections))
	for _, selection := range selections {
		if selection.OrgID == "" || selection.AppInstallationID == "" || selection.WorkloadID == "" ||
			selection.DeploymentSnapshotDigest == "" || servingConsumerProfile(selection.CapabilityID) == "" || seen[selection.CapabilityID] {
			return errors.New("runtime serving state: consumer deployment selection is incomplete or duplicated")
		}
		seen[selection.CapabilityID] = true
	}
	return nil
}

func (a *servingArtifactSet) consumerView(ctx context.Context, snapshotDigest catalogsnapshot.Digest,
	orgID, installationID, capability string, compatibility catalogsnapshot.Compatibility,
) (*catalogsnapshot.RuntimeMetadataView, error) {
	if a.store == nil || servingConsumerProfile(capability) == "" {
		return nil, errors.New("runtime serving state: consumer store or capability is invalid")
	}
	snapshot, err := a.snapshot(ctx, snapshotDigest)
	if err != nil {
		return nil, err
	}
	if snapshot.Digest() != snapshotDigest || snapshot.OrgID() != orgID {
		return nil, errors.New("runtime serving state: consumer snapshot identity is invalid")
	}
	projected, err := catalogsnapshot.ProjectRuntimeMetadataView(snapshot, catalogsnapshot.ProjectionKey{
		AppInstallationID: installationID, CapabilityID: capability,
	})
	if err != nil {
		return nil, err
	}
	view, err := catalogsnapshot.OpenRuntimeMetadataView(projected)
	if err != nil {
		return nil, err
	}
	if view.OrgID() != orgID || view.AppInstallationID() != installationID || view.CapabilityID() != capability ||
		view.Profile() != servingConsumerProfile(capability) || a.verifySource(view, snapshot) != nil || view.VerifyCompatibility(compatibility) != nil {
		return nil, errors.New("runtime serving state: consumer projection is incompatible or inconsistent")
	}
	return view, nil
}

func (s *ServingState) prepareConsumers(ctx context.Context, set *preparedServingSet, first *preparedServingState, artifacts *servingArtifactSet) error {
	for _, selection := range s.consumers {
		if selection.OrgID != first.selection.OrgID || selection.AppInstallationID != first.selection.AppInstallationID ||
			selection.DeploymentSnapshotDigest != first.deploymentSnapshotDigest || set.byCapability[selection.CapabilityID] != nil {
			return errors.New("runtime serving state: consumer does not share the deployment snapshot or duplicates a handler")
		}
		if first.sourceSnapshotDigest != selection.DeploymentSnapshotDigest {
			if _, err := artifacts.consumerView(ctx, selection.DeploymentSnapshotDigest,
				selection.OrgID, selection.AppInstallationID, selection.CapabilityID, first.selection.LocalCompatibility); err != nil {
				return fmt.Errorf("runtime serving state: invalid deployment consumer %s: %w", selection.CapabilityID, err)
			}
		}
		view, err := artifacts.consumerView(ctx, first.sourceSnapshotDigest,
			selection.OrgID, selection.AppInstallationID, selection.CapabilityID, first.selection.LocalCompatibility)
		if err != nil {
			return fmt.Errorf("runtime serving state: prepare consumer %s: %w", selection.CapabilityID, err)
		}
		switch selection.CapabilityID {
		case commandsemantic.CapabilityName:
			admission, err := NewCommandConsumerAdmission(ctx, CommandConsumerSelection{
				OrgID: selection.OrgID, AppInstallationID: selection.AppInstallationID,
				WorkloadID: selection.WorkloadID, RuntimeMetadataView: view,
			}, s.consumerViews)
			if err != nil {
				return err
			}
			set.commandConsumer = admission.prepared()
			if set.commandConsumer.current.Receipts.SecurityEpoch != first.receipts.SecurityEpoch {
				return errors.New("runtime serving state: command consumer security epoch does not match serving set")
			}
		case outboundsemantic.CapabilityName:
			admission, err := NewOutboundIntentAdmission(ctx, OutboundIntentSelection{
				OrgID: selection.OrgID, AppInstallationID: selection.AppInstallationID,
				WorkloadID: selection.WorkloadID, RuntimeMetadataView: view,
			}, s.consumerViews)
			if err != nil {
				return err
			}
			set.outboundConsumer = admission.prepared()
			if set.outboundConsumer.current.Receipts.SecurityEpoch != first.receipts.SecurityEpoch {
				return errors.New("runtime serving state: outbound consumer security epoch does not match serving set")
			}
		case pagelayoutsemantic.CapabilityName:
			epoch := first.receipts.SecurityEpoch
			admission, err := NewPageLayoutConsumerAdmission(PageLayoutConsumerSelection{
				OrgID: selection.OrgID, AppInstallationID: selection.AppInstallationID,
				WorkloadID: selection.WorkloadID, RuntimeMetadataView: view,
				CurrentSecurityEpoch: func() (uint64, bool) { return epoch, true },
			})
			if err != nil {
				return err
			}
			set.pageLayoutConsumer = admission.prepared()
		}
	}
	return nil
}

func (s *ServingState) hasConsumer(capability string) bool {
	if s != nil {
		for _, consumer := range s.consumers {
			if consumer.CapabilityID == capability {
				return true
			}
		}
	}
	return false
}

// The process retains these lightweight request adapters, never a startup view.
// Each request captures exactly one immutable consumer from the same atomic
// pointer used by ServingAdmission. Failed preparation makes all adapters fail.
func (s *ServingState) CommandConsumerAdmission() *CommandConsumerAdmission {
	if !s.hasConsumer(commandsemantic.CapabilityName) {
		return nil
	}
	return &CommandConsumerAdmission{prepared: func() *preparedCommandConsumer {
		if set := s.prepared.Load(); set != nil {
			return set.commandConsumer
		}
		return nil
	}}
}

func (s *ServingState) OutboundIntentAdmission() *OutboundIntentAdmission {
	if !s.hasConsumer(outboundsemantic.CapabilityName) {
		return nil
	}
	return &OutboundIntentAdmission{prepared: func() *preparedOutboundConsumer {
		if set := s.prepared.Load(); set != nil {
			return set.outboundConsumer
		}
		return nil
	}}
}

func (s *ServingState) PageLayoutConsumerAdmission() *PageLayoutConsumerAdmission {
	if !s.hasConsumer(pagelayoutsemantic.CapabilityName) {
		return nil
	}
	return &PageLayoutConsumerAdmission{prepared: func() *preparedPageLayoutConsumer {
		if set := s.prepared.Load(); set != nil {
			return set.pageLayoutConsumer
		}
		return nil
	}}
}
