package consumerv1

import (
	"context"
	"encoding/json"
	"regexp"
	"sort"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commandv1"
	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
)

var (
	digestPattern     = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
	packDigestPattern = regexp.MustCompile(`^nexus-pack-sha256-v1:[0-9a-f]{64}$`)
)

func ValidateGetRequest(ctx context.Context, request GetRequest) error {
	return validateContext(ctx)
}

func ValidateWatchRequest(ctx context.Context, request WatchRequest) error {
	return validateContext(ctx)
}

func ValidateAppMetadataView(view AppMetadataView) error {
	if !validOpaque(view.OrgID) ||
		appauthority.ValidateAppInstallationID(view.AppInstallationID) != nil ||
		appauthority.ValidateAppID(view.AppID) != nil ||
		appauthority.ValidateDigest(view.AppReleaseDigest) != nil ||
		!validLineage(view.Lineage) ||
		!validViewReceipts(view.Lineage, view.Receipts, AppMetadataCapabilityName) ||
		len(view.Components) > MaxComponentCount {
		return internal()
	}
	if !appMetadataViewWithinBudget(view) {
		return exhausted(MaxSemanticResponseBytes)
	}
	if err := validateResponseSize(view, MaxSemanticResponseBytes); err != nil {
		return err
	}
	for i, component := range view.Components {
		if ValidateAppMetadataComponent(component) != nil ||
			(i > 0 && component.ComponentID <= view.Components[i-1].ComponentID) {
			return internal()
		}
	}
	if view.Lineage.ViewDigest != ComputeAppMetadataViewDigest(view) {
		return internal()
	}
	return nil
}

func ValidateAppMetadataViewV2(view AppMetadataViewV2) error {
	return validateAppMetadataClosure(
		view.OrgID,
		view.AppInstallationID,
		view.AppID,
		view.AppReleaseDigest,
		view.Lineage,
		view.AppComponents,
		view.EffectiveComponents,
		view.Receipts,
		AppMetadataV2CapabilityName,
		appMetadataV2ViewDigestDomain,
	)
}

func ValidateAppMetadataViewV3(view AppMetadataViewV3) error {
	return validateAppMetadataClosure(
		view.OrgID,
		view.AppInstallationID,
		view.AppID,
		view.AppReleaseDigest,
		view.Lineage,
		view.AppComponents,
		view.EffectiveComponents,
		view.Receipts,
		AppMetadataV3CapabilityName,
		appMetadataV3ViewDigestDomain,
	)
}

func validateAppMetadataClosure(
	orgID string,
	appInstallationID string,
	appID string,
	appReleaseDigest string,
	lineage ViewLineage,
	appComponents []AppMetadataComponent,
	effectiveComponents []EffectiveMetadataComponent,
	receipts Receipts,
	capabilityName string,
	digestDomain string,
) error {
	maximumResponseBytes, ok := appMetadataSemanticResponseBytes(capabilityName)
	if !ok {
		return internal()
	}
	if !validOpaque(orgID) ||
		appauthority.ValidateAppInstallationID(appInstallationID) != nil ||
		appauthority.ValidateAppID(appID) != nil ||
		appauthority.ValidateDigest(appReleaseDigest) != nil ||
		!validLineage(lineage) ||
		!validViewReceipts(lineage, receipts, capabilityName) ||
		len(appComponents) > MaxComponentCount ||
		len(effectiveComponents) > MaxComponentCount ||
		len(appComponents)+len(effectiveComponents) > MaxComponentCount {
		return internal()
	}
	if !appMetadataClosureWithinBudget(
		orgID,
		appInstallationID,
		appID,
		appReleaseDigest,
		lineage,
		appComponents,
		effectiveComponents,
		receipts,
		maximumResponseBytes,
	) {
		return exhausted(maximumResponseBytes)
	}
	value := struct {
		OrgID               string
		AppInstallationID   string
		AppID               string
		AppReleaseDigest    string
		Lineage             ViewLineage
		AppComponents       []AppMetadataComponent
		EffectiveComponents []EffectiveMetadataComponent
		Receipts            Receipts
	}{
		OrgID: orgID, AppInstallationID: appInstallationID, AppID: appID,
		AppReleaseDigest: appReleaseDigest, Lineage: lineage,
		AppComponents: appComponents, EffectiveComponents: effectiveComponents,
		Receipts: receipts,
	}
	if err := validateResponseSize(value, maximumResponseBytes); err != nil {
		return err
	}
	for i, component := range appComponents {
		if ValidateAppMetadataComponentForCapability(component, capabilityName) != nil ||
			(i > 0 && component.ComponentID <= appComponents[i-1].ComponentID) {
			return internal()
		}
	}
	for i, component := range effectiveComponents {
		if ValidateEffectiveMetadataComponentForCapability(component, capabilityName) != nil ||
			(i > 0 && effectiveComponentSortKey(component) <=
				effectiveComponentSortKey(effectiveComponents[i-1])) {
			return internal()
		}
	}
	if lineage.ViewDigest != computeAppMetadataClosureDigest(
		digestDomain,
		orgID,
		appInstallationID,
		appID,
		appReleaseDigest,
		lineage,
		appComponents,
		effectiveComponents,
	) {
		return internal()
	}
	return nil
}

// Observe validates one monotonic App metadata watch stream. A stream is bound
// to one Org/AppInstallation/App identity; release and view digests may advance
// only with a strictly newer Catalog revision.
func (state *AppMetadataWatchState) Observe(view AppMetadataView) error {
	if state == nil || ValidateAppMetadataView(view) != nil ||
		view.Lineage.CatalogRevision <= state.lastCatalogRevision {
		return internal()
	}
	if state.seen {
		if view.OrgID != state.orgID || view.AppInstallationID != state.appInstallationID ||
			view.AppID != state.appID {
			return internal()
		}
	} else {
		state.seen = true
		state.orgID = view.OrgID
		state.appInstallationID = view.AppInstallationID
		state.appID = view.AppID
	}
	state.lastCatalogRevision = view.Lineage.CatalogRevision
	return nil
}

func (state *AppMetadataV2WatchState) Observe(view AppMetadataViewV2) error {
	if state == nil || ValidateAppMetadataViewV2(view) != nil ||
		view.Lineage.CatalogRevision <= state.lastCatalogRevision {
		return internal()
	}
	if state.seen {
		if view.OrgID != state.orgID || view.AppInstallationID != state.appInstallationID ||
			view.AppID != state.appID {
			return internal()
		}
	} else {
		state.seen = true
		state.orgID = view.OrgID
		state.appInstallationID = view.AppInstallationID
		state.appID = view.AppID
	}
	state.lastCatalogRevision = view.Lineage.CatalogRevision
	return nil
}

func (state *AppMetadataV3WatchState) Observe(view AppMetadataViewV3) error {
	if state == nil || ValidateAppMetadataViewV3(view) != nil ||
		view.Lineage.CatalogRevision <= state.lastCatalogRevision {
		return internal()
	}
	if state.seen {
		if view.OrgID != state.orgID || view.AppInstallationID != state.appInstallationID ||
			view.AppID != state.appID {
			return internal()
		}
	} else {
		state.seen = true
		state.orgID = view.OrgID
		state.appInstallationID = view.AppInstallationID
		state.appID = view.AppID
	}
	state.lastCatalogRevision = view.Lineage.CatalogRevision
	return nil
}

// ValidateAppMetadataComponent preserves the published V1/V2 1 MiB component
// ceiling. V3 callers must use ValidateAppMetadataComponentForCapability so the
// component and enclosing view are validated against the same identity.
func ValidateAppMetadataComponent(component AppMetadataComponent) error {
	return ValidateAppMetadataComponentForCapability(component, AppMetadataCapabilityName)
}

// ValidateAppMetadataComponentForCapability validates one App-owned component
// against the immutable response ceiling owned by the named App metadata
// capability. Unknown or unrelated capability identities fail closed.
func ValidateAppMetadataComponentForCapability(
	component AppMetadataComponent,
	capabilityName string,
) error {
	maximumResponseBytes, ok := appMetadataSemanticResponseBytes(capabilityName)
	if !ok {
		return internal()
	}
	return validateAppMetadataComponent(component, maximumResponseBytes)
}

func validateAppMetadataComponent(component AppMetadataComponent, maximumResponseBytes int) error {
	if len(component.CanonicalBehavior) == 0 || len(component.CanonicalBehavior) > maximumResponseBytes {
		return internal()
	}
	if err := appauthority.ValidateComponentProjection(appauthority.Component{
		ComponentID: component.ComponentID, ComponentKind: component.ComponentKind,
		ComponentFormatVersion: component.ComponentFormatVersion,
		NamespaceID:            component.NamespaceID, BusinessKey: component.BusinessKey,
		DefinitionVersion: component.DefinitionVersion, ContentDigest: component.ContentDigest,
	}, component.CanonicalBehavior); err != nil {
		return internal()
	}
	return nil
}

// ValidateEffectiveMetadataComponent preserves the published V2 1 MiB
// component ceiling. V3 callers must use
// ValidateEffectiveMetadataComponentForCapability.
func ValidateEffectiveMetadataComponent(component EffectiveMetadataComponent) error {
	return ValidateEffectiveMetadataComponentForCapability(
		component,
		AppMetadataV2CapabilityName,
	)
}

// ValidateEffectiveMetadataComponentForCapability validates one effective
// component against the immutable response ceiling owned by the named complete
// App metadata capability. V1 and unrelated identities fail closed because
// they do not carry effective components.
func ValidateEffectiveMetadataComponentForCapability(
	component EffectiveMetadataComponent,
	capabilityName string,
) error {
	if capabilityName != AppMetadataV2CapabilityName &&
		capabilityName != AppMetadataV3CapabilityName {
		return internal()
	}
	maximumResponseBytes, ok := appMetadataSemanticResponseBytes(capabilityName)
	if !ok {
		return internal()
	}
	return validateEffectiveMetadataComponent(component, maximumResponseBytes)
}

func validateEffectiveMetadataComponent(component EffectiveMetadataComponent, maximumResponseBytes int) error {
	if !validOpaque(component.ComponentKind) ||
		component.ComponentFormatVersion == 0 ||
		!validOpaque(component.BusinessKey) ||
		!digestPattern.MatchString(component.ContentDigest) ||
		len(component.CanonicalBehavior) == 0 ||
		len(component.CanonicalBehavior) > maximumResponseBytes ||
		component.ContentDigest !=
			ComputeEffectiveMetadataComponentDigest(component.CanonicalBehavior) {
		return internal()
	}
	return nil
}

func appMetadataSemanticResponseBytes(capabilityName string) (int, bool) {
	switch capabilityName {
	case AppMetadataCapabilityName, AppMetadataV2CapabilityName:
		return MaxSemanticResponseBytes, true
	case AppMetadataV3CapabilityName:
		return MaxAppMetadataV3SemanticResponseBytes, true
	default:
		return 0, false
	}
}

func effectiveComponentSortKey(component EffectiveMetadataComponent) string {
	return component.ComponentKind + "\x00" + component.BusinessKey
}

// ValidateIntegrationBindingView validates a view's closed shape, scope
// integrity and immutable lineage. It deliberately does not turn caller-made
// values into authority: serving must project from Catalog and Integration
// must exact-resolve every reference from its own installed-pack data source.
func ValidateIntegrationBindingView(view IntegrationBindingView) error {
	if !validOpaque(view.OrgID) ||
		appauthority.ValidateAppInstallationID(view.AppInstallationID) != nil ||
		!validOpaque(view.AuthorizedWorkloadID) ||
		!validLineage(view.Lineage) ||
		!validViewReceipts(view.Lineage, view.Receipts, IntegrationBindingCapabilityName) ||
		len(view.Bindings) > MaxBindingCount {
		return internal()
	}
	if !integrationBindingViewWithinBudget(view) {
		return exhausted(MaxSemanticResponseBytes)
	}
	if err := validateResponseSize(view, MaxSemanticResponseBytes); err != nil {
		return err
	}
	for i, binding := range view.Bindings {
		if validateIntegrationBinding(binding) != nil ||
			(i > 0 && bindingSortKey(binding) <= bindingSortKey(view.Bindings[i-1])) {
			return internal()
		}
	}
	if view.Lineage.ViewDigest != ComputeIntegrationBindingViewDigest(view) {
		return internal()
	}
	return nil
}

// Observe validates one monotonic Integration binding watch stream and rejects
// any cross-installation or cross-workload scope change.
func (state *IntegrationBindingWatchState) Observe(view IntegrationBindingView) error {
	if state == nil || ValidateIntegrationBindingView(view) != nil ||
		view.Lineage.CatalogRevision <= state.lastCatalogRevision {
		return internal()
	}
	if state.seen {
		if view.OrgID != state.orgID || view.AppInstallationID != state.appInstallationID ||
			view.AuthorizedWorkloadID != state.authorizedWorkloadID {
			return internal()
		}
	} else {
		state.seen = true
		state.orgID = view.OrgID
		state.appInstallationID = view.AppInstallationID
		state.authorizedWorkloadID = view.AuthorizedWorkloadID
	}
	state.lastCatalogRevision = view.Lineage.CatalogRevision
	return nil
}

func validateIntegrationBinding(binding IntegrationBinding) error {
	if !binding.Kind.Valid() || !validOpaque(binding.OwnerPack) ||
		!validOpaque(binding.PackInstallationID) || !validOpaque(binding.BusinessKey) ||
		binding.DefinitionVersion == 0 ||
		!packDigestPattern.MatchString(binding.PackPayloadDigest) ||
		len(binding.CredentialRefs) > MaxCredentialRefCount {
		return internal()
	}
	if !sort.StringsAreSorted(binding.CredentialRefs) {
		return internal()
	}
	for i, credential := range binding.CredentialRefs {
		if !validOpaque(credential) || (i > 0 && credential == binding.CredentialRefs[i-1]) {
			return internal()
		}
	}
	if binding.RuntimeCommand != nil {
		if (binding.Kind != BindingConnectorInboundRoute &&
			binding.Kind != BindingConnectorPushContract) ||
			!commandv1.ValidCommandKind(binding.RuntimeCommand.CommandKind) ||
			binding.RuntimeCommand.PayloadFormatVersion == 0 ||
			!validOpaque(binding.RuntimeCommand.DefinitionRef) ||
			!validOpaque(binding.RuntimeCommand.DefinitionVersion) {
			return internal()
		}
	}
	return nil
}

func bindingSortKey(binding IntegrationBinding) string {
	return string(binding.Kind) + "\x00" + binding.OwnerPack + "\x00" +
		binding.PackInstallationID + "\x00" + binding.BusinessKey
}

func validateContext(ctx context.Context) error {
	if ctx == nil {
		return invalid(common.InvalidComponentDeadline)
	}
	if err := ctx.Err(); err != nil {
		if err == context.DeadlineExceeded {
			return &Failure{Category: common.FailureDeadlineExceeded}
		}
		return &Failure{Category: common.FailureCanceled}
	}
	if _, ok := ctx.Deadline(); !ok {
		return invalid(common.InvalidComponentDeadline)
	}
	return nil
}

func validLineage(value ViewLineage) bool {
	return value.CatalogRevision > 0 && value.SchemaGeneration > 0 && value.EngineGeneration > 0 &&
		digestPattern.MatchString(value.SourceSnapshotDigest) &&
		digestPattern.MatchString(value.ViewDigest)
}

func validViewReceipts(lineage ViewLineage, receipts Receipts, capability string) bool {
	return common.ValidReceipts(receipts, capability) &&
		lineage.CatalogRevision == receipts.CatalogRevision &&
		lineage.SchemaGeneration == receipts.SchemaGeneration &&
		lineage.EngineGeneration == receipts.EngineGeneration
}

func validOpaque(value string) bool {
	if value == "" || len(value) > MaxIdentifierBytes || !utf8.ValidString(value) ||
		strings.TrimSpace(value) != value {
		return false
	}
	for _, r := range value {
		if unicode.IsControl(r) {
			return false
		}
	}
	return true
}

// semanticBudget bounds transport-independent content before any component
// digest or complete-view digest work. The framing charges mirror the
// length-prefixed digest grammar and include serving receipts, so accepted
// views cannot make validation hash an unbounded projection.
type semanticBudget struct {
	remaining int
	exceeded  bool
}

func newSemanticBudget(maximum int) semanticBudget {
	return semanticBudget{remaining: maximum}
}

func (budget *semanticBudget) add(size int) {
	if budget.exceeded || size < 0 || size > budget.remaining {
		budget.exceeded = true
		budget.remaining = 0
		return
	}
	budget.remaining -= size
}

func (budget *semanticBudget) addString(value string) {
	budget.add(8)
	budget.add(len(value))
}

func (budget *semanticBudget) addBytes(value []byte) {
	budget.add(8)
	budget.add(len(value))
}

func (budget *semanticBudget) addLineage(lineage ViewLineage) {
	budget.add(8 * 3)
	budget.addString(lineage.SourceSnapshotDigest)
	budget.addString(lineage.ViewDigest)
}

func (budget *semanticBudget) addReceipts(receipts Receipts) {
	budget.addString(receipts.ProtocolVersion)
	budget.add(8 * 4)
	budget.addString(receipts.CapabilityVersion)
}

func (budget *semanticBudget) addRuntimeCommandBinding(
	binding *RuntimeCommandBinding,
) {
	if binding == nil {
		return
	}
	budget.add(4)
	budget.addString(binding.CommandKind)
	budget.add(4)
	budget.addString(binding.DefinitionRef)
	budget.addString(binding.DefinitionVersion)
}

func appMetadataViewWithinBudget(view AppMetadataView) bool {
	budget := newSemanticBudget(MaxSemanticResponseBytes)
	budget.addString(view.OrgID)
	budget.addString(view.AppInstallationID)
	budget.addString(view.AppID)
	budget.addString(view.AppReleaseDigest)
	budget.addLineage(view.Lineage)
	budget.add(8)
	for _, component := range view.Components {
		budget.addString(component.ComponentID)
		budget.addString(component.ComponentKind)
		budget.add(4)
		budget.addString(component.NamespaceID)
		budget.addString(component.BusinessKey)
		budget.addString(component.DefinitionVersion)
		budget.addString(component.ContentDigest)
		budget.addBytes(component.CanonicalBehavior)
		if budget.exceeded {
			return false
		}
	}
	budget.addReceipts(view.Receipts)
	return !budget.exceeded
}

func appMetadataClosureWithinBudget(
	orgID string,
	appInstallationID string,
	appID string,
	appReleaseDigest string,
	lineage ViewLineage,
	appComponents []AppMetadataComponent,
	effectiveComponents []EffectiveMetadataComponent,
	receipts Receipts,
	maximum int,
) bool {
	budget := newSemanticBudget(maximum)
	budget.addString(orgID)
	budget.addString(appInstallationID)
	budget.addString(appID)
	budget.addString(appReleaseDigest)
	budget.addLineage(lineage)
	budget.add(8)
	for _, component := range appComponents {
		budget.addString(component.ComponentID)
		budget.addString(component.ComponentKind)
		budget.add(4)
		budget.addString(component.NamespaceID)
		budget.addString(component.BusinessKey)
		budget.addString(component.DefinitionVersion)
		budget.addString(component.ContentDigest)
		budget.addBytes(component.CanonicalBehavior)
		if budget.exceeded {
			return false
		}
	}
	budget.add(8)
	for _, component := range effectiveComponents {
		budget.addString(component.ComponentKind)
		budget.add(4)
		budget.addString(component.BusinessKey)
		budget.addString(component.ContentDigest)
		budget.addBytes(component.CanonicalBehavior)
		if budget.exceeded {
			return false
		}
	}
	budget.addReceipts(receipts)
	return !budget.exceeded
}

func integrationBindingViewWithinBudget(view IntegrationBindingView) bool {
	budget := newSemanticBudget(MaxSemanticResponseBytes)
	budget.addString(view.OrgID)
	budget.addString(view.AppInstallationID)
	budget.addString(view.AuthorizedWorkloadID)
	budget.addLineage(view.Lineage)
	budget.add(8)
	for _, binding := range view.Bindings {
		budget.addString(string(binding.Kind))
		budget.addString(binding.OwnerPack)
		budget.addString(binding.PackInstallationID)
		budget.addString(binding.PackPayloadDigest)
		budget.addString(binding.BusinessKey)
		budget.add(8)
		budget.add(8)
		for _, credential := range binding.CredentialRefs {
			budget.addString(credential)
			if budget.exceeded {
				return false
			}
		}
		budget.addRuntimeCommandBinding(binding.RuntimeCommand)
		if budget.exceeded {
			return false
		}
	}
	budget.addReceipts(view.Receipts)
	return !budget.exceeded
}

func validateResponseSize(value any, maximum int) error {
	encoded, err := json.Marshal(value)
	if err != nil {
		return internal()
	}
	if len(encoded) > maximum {
		return exhausted(maximum)
	}
	return nil
}

func invalid(component common.InvalidComponent) *Failure {
	return &Failure{Category: common.FailureInvalidRequest, InvalidComponent: component}
}

func exhausted(maximum int) *Failure {
	return &Failure{
		Category:  common.FailureResourceExhausted,
		LimitKind: common.LimitSemanticResponse,
		Maximum:   uint64(maximum),
	}
}

func internal() *Failure { return &Failure{Category: common.FailureInternal} }
