package consumerv1

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"
	"unicode/utf8"
)

var (
	effectiveStandardFlowPrefix    = []byte(`{"definitionVersion":"`)
	effectiveStandardFlowSeparator = []byte(`","canonicalBehavior":`)
)

// EffectiveStandardFlowComponentKind names a Catalog-derived effective Flow
// that is authorized by an AppRelease-owned standard-Flow component with the
// same BusinessKey. It carries only the effective version and behavior; App
// ownership remains in AppComponents.
const EffectiveStandardFlowComponentKind = "metadata.effective.standard-flow.v1"

// EffectiveStandardFlowDocument is the opaque v1 behavior carried by an
// EffectiveMetadataComponent of EffectiveStandardFlowComponentKind.
type EffectiveStandardFlowDocument struct {
	DefinitionVersion string          `json:"definitionVersion"`
	CanonicalBehavior json.RawMessage `json:"canonicalBehavior"`
}

// EncodeEffectiveStandardFlowDocument returns the canonical v1 envelope for
// one effective standard Flow. The Flow behavior is intentionally opaque to
// this neutral contract.
func EncodeEffectiveStandardFlowDocument(
	definitionVersion string,
	canonicalBehavior []byte,
) ([]byte, error) {
	if err := validateEffectiveStandardFlowDefinitionVersion(definitionVersion); err != nil {
		return nil, err
	}
	if len(canonicalBehavior) == 0 || len(canonicalBehavior) > MaxSemanticResponseBytes ||
		!utf8.Valid(canonicalBehavior) ||
		!json.Valid(canonicalBehavior) {
		return nil, fmt.Errorf("effective standard Flow behavior is invalid")
	}
	// CanonicalBehavior is already an immutable, digest-bound byte identity.
	// Do not pass it through json.Marshal as RawMessage: that compacts and
	// HTML-escapes otherwise valid JSON and changes the behavior being carried.
	// definitionVersion is restricted to canonical decimal digits above, so the
	// fixed envelope can safely preserve the opaque behavior byte-for-byte.
	encoded := make([]byte, 0, len(canonicalBehavior)+64)
	encoded = append(encoded, effectiveStandardFlowPrefix...)
	encoded = append(encoded, definitionVersion...)
	encoded = append(encoded, effectiveStandardFlowSeparator...)
	encoded = append(encoded, canonicalBehavior...)
	encoded = append(encoded, '}')
	if len(encoded) > MaxSemanticResponseBytes {
		return nil, fmt.Errorf("effective standard Flow envelope exceeds the response budget")
	}
	return encoded, nil
}

// DecodeEffectiveStandardFlowDocument validates and decodes one canonical v1
// envelope. Re-encoding equality prevents multiple byte identities for the
// same effective fact.
func DecodeEffectiveStandardFlowDocument(
	encoded []byte,
) (EffectiveStandardFlowDocument, error) {
	var document EffectiveStandardFlowDocument
	if len(encoded) == 0 || len(encoded) > MaxSemanticResponseBytes {
		return document, fmt.Errorf("effective standard Flow envelope is invalid")
	}
	if !bytes.HasPrefix(encoded, effectiveStandardFlowPrefix) || encoded[len(encoded)-1] != '}' {
		return document, fmt.Errorf("effective standard Flow envelope is not canonical")
	}
	remainder := encoded[len(effectiveStandardFlowPrefix):]
	separator := bytes.Index(remainder, effectiveStandardFlowSeparator)
	if separator <= 0 {
		return document, fmt.Errorf("effective standard Flow envelope is not canonical")
	}
	document.DefinitionVersion = string(remainder[:separator])
	behaviorStart := separator + len(effectiveStandardFlowSeparator)
	document.CanonicalBehavior = append(json.RawMessage(nil), remainder[behaviorStart:len(remainder)-1]...)
	canonical, err := EncodeEffectiveStandardFlowDocument(
		document.DefinitionVersion,
		document.CanonicalBehavior,
	)
	if err != nil {
		return EffectiveStandardFlowDocument{}, err
	}
	if !bytes.Equal(canonical, encoded) {
		return EffectiveStandardFlowDocument{}, fmt.Errorf("effective standard Flow envelope is not canonical")
	}
	return document, nil
}

func validateEffectiveStandardFlowDefinitionVersion(value string) error {
	version, err := strconv.ParseUint(value, 10, 64)
	if err != nil || version == 0 || strconv.FormatUint(version, 10) != value {
		return fmt.Errorf("effective standard Flow definition version is invalid")
	}
	return nil
}
