package serviceconfiguration

import (
	"fmt"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	catalogv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
)

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

type credentialGenerationIdentity interface {
	ID() string
	StableCredentialIdentity() string
	ProviderScopeID() string
	ConnectorAccountID() string
}

func validateAuthoringMaterial(
	input AuthoringInput,
	view catalogv1.IntegrationBindingView,
	credentialGenerations []credentialGenerationIdentity,
) error {
	if err := validateCatalogIntegrationBindingView(view); err != nil {
		return err
	}
	catalog := catalogReferenceFromView(view)
	if err := validateCatalogReference(catalog); err != nil {
		return invalidGeneration("Catalog reference: %v", err)
	}
	if err := validateAuthoringInputShape(input, len(view.Bindings)); err != nil {
		return err
	}
	if size, ok := authoringCanonicalSize(input, catalog); !ok || size > MaxCanonicalGenerationBytes {
		return invalidGeneration(
			"canonical generation exceeds %d bytes",
			MaxCanonicalGenerationBytes,
		)
	}

	for accountIndex, account := range input.Accounts {
		for bindingIndex, configuration := range account.Bindings {
			binding := view.Bindings[configuration.CatalogBindingOrdinal]
			if err := validateSelectionsAgainstBinding(configuration, binding); err != nil {
				return invalidGeneration(
					"accounts[%d].bindings[%d]: %v",
					accountIndex,
					bindingIndex,
					err,
				)
			}
		}
	}

	if credentialGenerations == nil {
		return invalidGeneration("exact credential generation set must be present")
	}
	if len(credentialGenerations) > MaxCredentialSelections {
		return invalidGeneration(
			"exact credential generation set exceeds %d entries",
			MaxCredentialSelections,
		)
	}
	byID := make(map[string]credentialGenerationIdentity, len(credentialGenerations))
	for index, generation := range credentialGenerations {
		if !validIdentifier(generation.ID()) ||
			!validIdentifier(generation.StableCredentialIdentity()) ||
			!validIdentifier(generation.ConnectorAccountID()) ||
			!validIdentifier(generation.ProviderScopeID()) {
			return invalidGeneration(
				"exact credential generation %d has an invalid identity projection",
				index,
			)
		}
		if _, duplicate := byID[generation.ID()]; duplicate {
			return invalidGeneration("exact credential generation set contains a duplicate ID")
		}
		byID[generation.ID()] = generation
	}

	required := make(map[string]struct{}, len(byID))
	for _, account := range input.Accounts {
		for _, binding := range account.Bindings {
			for _, selection := range binding.CredentialSelections {
				generation, exists := byID[selection.CredentialGenerationID]
				if !exists {
					return fmt.Errorf(
						"%w: %q",
						ErrCredentialGenerationUnavailable,
						selection.CredentialGenerationID,
					)
				}
				if generation.StableCredentialIdentity() != selection.CredentialRef ||
					generation.ConnectorAccountID() != account.ConnectorAccountID ||
					generation.ProviderScopeID() != account.ProviderScopeID {
					return invalidGeneration(
						"credential generation %q disagrees with its account, provider scope, or CredentialRef",
						selection.CredentialGenerationID,
					)
				}
				required[generation.ID()] = struct{}{}
			}
		}
	}
	if len(required) != len(byID) {
		return invalidGeneration("exact credential generation set contains an unreferenced ID")
	}
	return nil
}

func validateCatalogIntegrationBindingView(view catalogv1.IntegrationBindingView) error {
	if err := catalogv1.ValidateIntegrationBindingView(view); err != nil {
		return invalidGeneration("Catalog IntegrationBindingView: %v", err)
	}
	if view.Lineage.ViewDigest != catalogv1.ComputeIntegrationBindingViewDigest(view) {
		return invalidGeneration("Catalog IntegrationBindingView digest disagrees")
	}
	return nil
}

func validateStoredSource(input AuthoringInput, catalog catalogReference) error {
	if err := validateCatalogReference(catalog); err != nil {
		return invalidGeneration("Catalog reference: %v", err)
	}
	if err := validateAuthoringInputShape(input, MaxBindingsPerAccount); err != nil {
		return err
	}
	if size, ok := authoringCanonicalSize(input, catalog); !ok || size > MaxCanonicalGenerationBytes {
		return invalidGeneration(
			"canonical generation exceeds %d bytes",
			MaxCanonicalGenerationBytes,
		)
	}
	return nil
}

func validateAuthoringInputShape(input AuthoringInput, catalogBindingCount int) error {
	if !validIdentifier(input.ID) {
		return invalidGeneration("generation ID is not canonical")
	}
	if err := validateExactOwnerRevision("owner revision", input.OwnerRevision); err != nil {
		return invalidGeneration("%v", err)
	}
	if input.Accounts == nil {
		return invalidGeneration("accounts must be present")
	}
	if len(input.Accounts) > MaxAccounts {
		return invalidGeneration("accounts exceed %d entries", MaxAccounts)
	}

	totalBindings := 0
	totalSelections := 0
	seenAccounts := make(map[string]struct{}, len(input.Accounts))
	for accountIndex, account := range input.Accounts {
		if !validIdentifier(account.ConnectorAccountID) {
			return invalidGeneration("accounts[%d].connector account ID is not canonical", accountIndex)
		}
		if !validIdentifier(account.ProviderScopeID) {
			return invalidGeneration("accounts[%d].provider scope ID is not canonical", accountIndex)
		}
		if err := validateExactOwnerRevision("account revision", account.AccountRevision); err != nil {
			return invalidGeneration("accounts[%d]: %v", accountIndex, err)
		}
		if err := validateExactOwnerRevision("provider revision", account.ProviderRevision); err != nil {
			return invalidGeneration("accounts[%d]: %v", accountIndex, err)
		}
		if account.Bindings == nil {
			return invalidGeneration("accounts[%d].bindings must be present", accountIndex)
		}
		if len(account.Bindings) > MaxBindingsPerAccount ||
			len(account.Bindings) > MaxBindingConfigurations-totalBindings {
			return invalidGeneration("binding configurations exceed their v1 limit")
		}
		totalBindings += len(account.Bindings)
		if _, duplicate := seenAccounts[account.ConnectorAccountID]; duplicate {
			return invalidGeneration("accounts contain a duplicate connector account ID")
		}
		seenAccounts[account.ConnectorAccountID] = struct{}{}

		seenOrdinals := make(map[uint32]struct{}, len(account.Bindings))
		for bindingIndex, binding := range account.Bindings {
			if uint64(binding.CatalogBindingOrdinal) >= uint64(catalogBindingCount) {
				return invalidGeneration(
					"accounts[%d].bindings[%d] references an absent Catalog binding ordinal",
					accountIndex,
					bindingIndex,
				)
			}
			if err := validateExactOwnerRevision(
				"configuration revision",
				binding.ConfigurationRevision,
			); err != nil {
				return invalidGeneration("accounts[%d].bindings[%d]: %v", accountIndex, bindingIndex, err)
			}
			if !validIdentifier(binding.ConfigurationFormatVersion) {
				return invalidGeneration(
					"accounts[%d].bindings[%d].configuration format version is not canonical",
					accountIndex,
					bindingIndex,
				)
			}
			if len(binding.CanonicalConfiguration) > MaxCanonicalValueBytes {
				return invalidGeneration(
					"accounts[%d].bindings[%d].canonical configuration exceeds %d bytes",
					accountIndex,
					bindingIndex,
					MaxCanonicalValueBytes,
				)
			}
			if binding.CredentialSelections == nil {
				return invalidGeneration(
					"accounts[%d].bindings[%d].credential selections must be present",
					accountIndex,
					bindingIndex,
				)
			}
			if len(binding.CredentialSelections) > catalogv1.MaxCredentialRefCount ||
				len(binding.CredentialSelections) > MaxCredentialSelections-totalSelections {
				return invalidGeneration("credential selections exceed their v1 limit")
			}
			totalSelections += len(binding.CredentialSelections)
			if _, duplicate := seenOrdinals[binding.CatalogBindingOrdinal]; duplicate {
				return invalidGeneration(
					"accounts[%d].bindings contain a duplicate Catalog binding ordinal",
					accountIndex,
				)
			}
			seenOrdinals[binding.CatalogBindingOrdinal] = struct{}{}

			seenRefs := make(map[string]struct{}, len(binding.CredentialSelections))
			for selectionIndex, selection := range binding.CredentialSelections {
				if !validIdentifier(selection.CredentialRef) {
					return invalidGeneration(
						"accounts[%d].bindings[%d].credential selections[%d].CredentialRef is not canonical",
						accountIndex,
						bindingIndex,
						selectionIndex,
					)
				}
				if !validIdentifier(selection.CredentialGenerationID) {
					return invalidGeneration(
						"accounts[%d].bindings[%d].credential selections[%d].generation ID is not canonical",
						accountIndex,
						bindingIndex,
						selectionIndex,
					)
				}
				if _, duplicate := seenRefs[selection.CredentialRef]; duplicate {
					return invalidGeneration(
						"accounts[%d].bindings[%d].credential selections contain a duplicate CredentialRef",
						accountIndex,
						bindingIndex,
					)
				}
				seenRefs[selection.CredentialRef] = struct{}{}
			}
		}
	}
	return nil
}

func validateSelectionsAgainstBinding(
	configuration BindingInput,
	binding catalogv1.IntegrationBinding,
) error {
	if len(configuration.CredentialSelections) != len(binding.CredentialRefs) {
		return fmt.Errorf("credential selections must contain exactly one entry per Catalog CredentialRef")
	}
	selections := make(map[string]struct{}, len(configuration.CredentialSelections))
	for _, selection := range configuration.CredentialSelections {
		selections[selection.CredentialRef] = struct{}{}
	}
	for _, credentialRef := range binding.CredentialRefs {
		if _, exists := selections[credentialRef]; !exists {
			return fmt.Errorf("credential selections omit Catalog CredentialRef %q", credentialRef)
		}
	}
	return nil
}

func validateCatalogReference(catalog catalogReference) error {
	if !validIdentifier(catalog.orgID) {
		return fmt.Errorf("OrgID is not canonical")
	}
	if err := appauthority.ValidateAppInstallationID(catalog.appInstallationID); err != nil {
		return fmt.Errorf("AppInstallationID: %w", err)
	}
	if !validIdentifier(catalog.authorizedWorkloadID) {
		return fmt.Errorf("AuthorizedWorkloadID is not canonical")
	}
	if catalog.lineage.CatalogRevision == 0 ||
		catalog.lineage.SchemaGeneration == 0 ||
		catalog.lineage.EngineGeneration == 0 ||
		!validDigest(catalog.lineage.SourceSnapshotDigest) ||
		!validDigest(catalog.lineage.ViewDigest) {
		return fmt.Errorf("lineage is not canonical")
	}
	if catalog.receipts.ProtocolVersion != "1.0" ||
		catalog.receipts.CapabilityVersion != catalogv1.IntegrationBindingCapabilityName ||
		catalog.receipts.CatalogRevision != catalog.lineage.CatalogRevision ||
		catalog.receipts.SchemaGeneration != catalog.lineage.SchemaGeneration ||
		catalog.receipts.EngineGeneration != catalog.lineage.EngineGeneration {
		return fmt.Errorf("receipt tuple does not attest the exact lineage")
	}
	return nil
}

func validateExactOwnerRevision(name string, revision ExactOwnerRevision) error {
	if !validIdentifier(revision.RevisionID) {
		return fmt.Errorf("%s ID is not canonical", name)
	}
	if !validDigest(revision.ValueDigest) {
		return fmt.Errorf("%s digest is not canonical sha256", name)
	}
	return nil
}

func validIdentifier(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 validDigest(value string) bool { return prefixedSHA256Pattern.MatchString(value) }

func invalidGeneration(format string, args ...any) error {
	return fmt.Errorf("%w: %s", ErrInvalidGeneration, fmt.Sprintf(format, args...))
}

func storedGenerationInvalid(format string, args ...any) error {
	return fmt.Errorf("%w: %s", ErrStoredGenerationInvalid, fmt.Sprintf(format, args...))
}
