package connectorbindingownerv1

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

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

// ValidateIdentity validates only the closed portable identity grammar. It
// does not prove that the named pack generation exists or is authoritative.
func ValidateIdentity(identity Identity) error {
	if !identity.Kind.Valid() {
		return fmt.Errorf("Identity.Kind is unsupported")
	}
	for _, field := range []struct {
		name  string
		value string
	}{
		{name: "OwnerPack", value: identity.OwnerPack},
		{name: "PackInstallationID", value: identity.PackInstallationID},
		{name: "BusinessKey", value: identity.BusinessKey},
	} {
		if !validOpaqueIdentifier(field.value) {
			return fmt.Errorf("Identity.%s is not canonical", field.name)
		}
	}
	if !packPayloadDigestPattern.MatchString(identity.PackPayloadDigest) {
		return fmt.Errorf("Identity.PackPayloadDigest is invalid")
	}
	if identity.DefinitionVersion == 0 {
		return fmt.Errorf("Identity.DefinitionVersion must be positive")
	}
	return nil
}

// ValidateDefinitionEnvelope rederives every digest and receipt from the exact
// identity and opaque definition bytes and rejects any disagreement.
func ValidateDefinitionEnvelope(value DefinitionEnvelope) error {
	if value.EnvelopeVersion != EnvelopeVersion {
		return fmt.Errorf("DefinitionEnvelope.EnvelopeVersion must equal %q", EnvelopeVersion)
	}
	if value.ReceiptVersion != ReceiptVersion {
		return fmt.Errorf("DefinitionEnvelope.ReceiptVersion must equal %q", ReceiptVersion)
	}
	if err := validateSource(value.Identity, value.CanonicalDefinition); err != nil {
		return err
	}
	if !prefixedSHA256Pattern.MatchString(value.CanonicalDefinitionDigest) {
		return fmt.Errorf("DefinitionEnvelope.CanonicalDefinitionDigest is invalid")
	}
	if !prefixedSHA256Pattern.MatchString(value.OwnerReceiptDigest) {
		return fmt.Errorf("DefinitionEnvelope.OwnerReceiptDigest is invalid")
	}
	expected := deriveEnvelope(value.Identity, value.CanonicalDefinition)
	if value.CanonicalDefinitionDigest != expected.CanonicalDefinitionDigest {
		return fmt.Errorf("DefinitionEnvelope.CanonicalDefinitionDigest disagrees with canonical definition")
	}
	if !bytes.Equal(value.OwnerReceipt, expected.OwnerReceipt) {
		return fmt.Errorf("DefinitionEnvelope.OwnerReceipt disagrees with exact owner identity")
	}
	if value.OwnerReceiptDigest != expected.OwnerReceiptDigest {
		return fmt.Errorf("DefinitionEnvelope.OwnerReceiptDigest disagrees with owner receipt")
	}
	return nil
}

// MarshalDefinitionEnvelopeJSON emits the one canonical JSON representation
// accepted by ParseDefinitionEnvelopeJSON.
func MarshalDefinitionEnvelopeJSON(value DefinitionEnvelope) ([]byte, error) {
	if err := ValidateDefinitionEnvelope(value); err != nil {
		return nil, err
	}
	raw, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}
	if len(raw) > MaxSerializedEnvelopeBytes {
		return nil, fmt.Errorf("serialized definition envelope exceeds %d bytes", MaxSerializedEnvelopeBytes)
	}
	return raw, nil
}

// ParseDefinitionEnvelopeJSON strictly decodes one bounded canonical envelope.
// Unknown fields, trailing data, alternate whitespace, and alternate field
// ordering fail closed so persisted bytes have one read path.
func ParseDefinitionEnvelopeJSON(raw []byte) (DefinitionEnvelope, error) {
	if len(raw) == 0 || len(raw) > MaxSerializedEnvelopeBytes {
		return DefinitionEnvelope{}, fmt.Errorf("serialized definition envelope size is invalid")
	}
	decoder := json.NewDecoder(bytes.NewReader(raw))
	decoder.DisallowUnknownFields()
	var value DefinitionEnvelope
	if err := decoder.Decode(&value); err != nil {
		return DefinitionEnvelope{}, fmt.Errorf("decode definition envelope: %w", err)
	}
	if err := ensureJSONEOF(decoder); err != nil {
		return DefinitionEnvelope{}, fmt.Errorf("decode definition envelope: %w", err)
	}
	if err := ValidateDefinitionEnvelope(value); err != nil {
		return DefinitionEnvelope{}, err
	}
	canonical, err := json.Marshal(value)
	if err != nil {
		return DefinitionEnvelope{}, err
	}
	if !bytes.Equal(raw, canonical) {
		return DefinitionEnvelope{}, fmt.Errorf("serialized definition envelope is not canonical JSON")
	}
	return CloneDefinitionEnvelope(value), nil
}

func validateSource(identity Identity, canonicalDefinition []byte) error {
	if err := ValidateIdentity(identity); err != nil {
		return err
	}
	if len(canonicalDefinition) == 0 {
		return fmt.Errorf("DefinitionEnvelope.CanonicalDefinition is required")
	}
	if len(canonicalDefinition) > MaxCanonicalDefinitionBytes {
		return fmt.Errorf("DefinitionEnvelope.CanonicalDefinition exceeds %d bytes", MaxCanonicalDefinitionBytes)
	}
	return nil
}

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

func ensureJSONEOF(decoder *json.Decoder) error {
	var trailing any
	if err := decoder.Decode(&trailing); err != io.EOF {
		if err == nil {
			return fmt.Errorf("unexpected trailing JSON value")
		}
		return err
	}
	return nil
}
