package credentialgeneration

import (
	"bytes"
	"errors"
)

var (
	// ErrDependencyMissing reports that a required repository dependency is
	// absent.
	ErrDependencyMissing = errors.New("connector credential generation: dependency missing")
	// ErrUnsupportedReceiptProtocol reports an omitted or unknown exact owner-
	// receipt protocol version.
	ErrUnsupportedReceiptProtocol = errors.New("connector credential generation: unsupported receipt protocol")
	// ErrInvalidGeneration reports that caller-authored generation material is
	// outside the closed credential-generation contract.
	ErrInvalidGeneration = errors.New("connector credential generation: invalid generation")
	// ErrIdentityConflict reports that an existing generation ID names
	// materially different immutable bytes.
	ErrIdentityConflict = errors.New("connector credential generation: identity conflict")
	// ErrStoredGenerationInvalid reports that a persisted row does not
	// reconstruct one complete canonical generation.
	ErrStoredGenerationInvalid = errors.New("connector credential generation: stored generation invalid")
	// ErrSchemaUnavailable reports that the NexusCRM-owned physical schema is
	// absent or has drifted from the exact repository contract.
	ErrSchemaUnavailable = errors.New("connector credential generation: schema unavailable")
	// ErrExactSetBudgetExceeded reports that an exact generation-set read would
	// exceed its closed SQL-work, input, retained-identity, or payload budget.
	ErrExactSetBudgetExceeded = errors.New("connector credential generation: exact-set budget exceeded")
)

// ReceiptProtocolVersion identifies one immutable credential-generation owner
// receipt recipe. Values are exact and case-sensitive.
type ReceiptProtocolVersion string

const (
	// ReceiptProtocolV1 is the first closed credential-generation owner receipt
	// protocol. Callers must select it explicitly; empty never means v1.
	ReceiptProtocolV1 ReceiptProtocolVersion = "nexuscrm.connector-cred-gen.v1"

	// MaxTransportConfigurationBytes bounds the exact non-secret transport
	// configuration retained twice in a row: once as configuration and once in
	// the deterministic owner receipt. One MiB remains below the TiDB row-entry
	// budget used by the Connector's other bounded owner receipts.
	MaxTransportConfigurationBytes = 1 << 20

	// Exact generation-set reads are a publication admission primitive, not an
	// unbounded list API. These independent ceilings bound SQL batches, caller
	// input, retained identity strings, and validated binary payload.
	MaxExactIdentitySetEntries       = 4_096
	MaxExactIdentitySetIDBytes       = 512 << 10
	MaxExactIdentitySetIdentityBytes = 4 << 20
	MaxExactIdentitySetPayloadBytes  = 16 << 20
)

// AuthoringInput is the complete L2-authored immutable generation fact.
// TransportConfiguration must contain canonical non-secret bytes; this neutral
// package preserves it exactly and does not interpret provider-specific shape.
type AuthoringInput struct {
	ReceiptProtocolVersion   ReceiptProtocolVersion
	ID                       string
	StableCredentialIdentity string
	ProviderScopeID          string
	ConnectorAccountID       string
	SecretBackendProtocol    string
	SecretBackendVersionRef  string
	TransportConfiguration   []byte
}

// SecretBackendReference is the exact opaque coordinate of immutable secret
// material. It exposes neither a mutable name nor a latest/current lookup.
type SecretBackendReference struct {
	protocol   string
	versionRef string
}

// Protocol returns the exact versioned backend protocol.
func (reference SecretBackendReference) Protocol() string { return reference.protocol }

// VersionRef returns the exact immutable backend version reference.
func (reference SecretBackendReference) VersionRef() string { return reference.versionRef }

// Generation is one immutable credential-generation fact. All fields remain
// private so digests and receipts cannot be supplied independently of their
// canonical source bytes.
type Generation struct {
	receiptProtocolVersion       ReceiptProtocolVersion
	id                           string
	stableCredentialIdentity     string
	providerScopeID              string
	connectorAccountID           string
	secretBackendProtocol        string
	secretBackendVersionRef      string
	transportConfiguration       []byte
	transportConfigurationDigest string
	ownerReceipt                 []byte
	ownerReceiptDigest           string
}

// ExactIdentity is the immutable identity projection of one complete exact-
// read credential generation. Its fields are private so callers cannot mint
// an authoritative projection; Repository.ResolveExactIdentitySet is its sole
// production issuer.
type ExactIdentity struct {
	id                       string
	stableCredentialIdentity string
	providerScopeID          string
	connectorAccountID       string
}

// ID returns the exact immutable generation identity.
func (identity ExactIdentity) ID() string { return identity.id }

// StableCredentialIdentity returns the stable credential identity shared by
// rotation generations.
func (identity ExactIdentity) StableCredentialIdentity() string {
	return identity.stableCredentialIdentity
}

// ProviderScopeID returns the opaque provider-account scope identity.
func (identity ExactIdentity) ProviderScopeID() string { return identity.providerScopeID }

// ConnectorAccountID returns the exact connector-account identity.
func (identity ExactIdentity) ConnectorAccountID() string { return identity.connectorAccountID }

func (generation Generation) exactIdentity() ExactIdentity {
	return ExactIdentity{
		id:                       generation.id,
		stableCredentialIdentity: generation.stableCredentialIdentity,
		providerScopeID:          generation.providerScopeID,
		connectorAccountID:       generation.connectorAccountID,
	}
}

// ReceiptProtocolVersion returns the exact owner-receipt protocol.
func (generation Generation) ReceiptProtocolVersion() ReceiptProtocolVersion {
	return generation.receiptProtocolVersion
}

// ID returns the exact immutable generation identity.
func (generation Generation) ID() string { return generation.id }

// StableCredentialIdentity returns the stable L2 credential identity shared by
// rotation generations.
func (generation Generation) StableCredentialIdentity() string {
	return generation.stableCredentialIdentity
}

// ProviderScopeID returns the opaque provider-account scope identity.
func (generation Generation) ProviderScopeID() string { return generation.providerScopeID }

// ConnectorAccountID returns the exact L2 connector-account identity.
func (generation Generation) ConnectorAccountID() string { return generation.connectorAccountID }

// SecretBackendProtocol returns the exact backend protocol selector.
func (generation Generation) SecretBackendProtocol() string {
	return generation.secretBackendProtocol
}

// SecretBackendVersionRef returns the immutable backend version coordinate.
func (generation Generation) SecretBackendVersionRef() string {
	return generation.secretBackendVersionRef
}

// SecretBackendReference returns the exact opaque backend coordinate.
func (generation Generation) SecretBackendReference() SecretBackendReference {
	return SecretBackendReference{
		protocol:   generation.secretBackendProtocol,
		versionRef: generation.secretBackendVersionRef,
	}
}

// TransportConfiguration returns a defensive copy of the canonical non-secret
// transport bytes.
func (generation Generation) TransportConfiguration() []byte {
	return cloneBinary(generation.transportConfiguration)
}

// TransportConfigurationByteCount returns the exact retained byte count
// without copying the bounded opaque transport payload.
func (generation Generation) TransportConfigurationByteCount() uint64 {
	return uint64(len(generation.transportConfiguration))
}

// TransportConfigurationDigest returns the owner-protocol digest of the exact
// transport bytes.
func (generation Generation) TransportConfigurationDigest() string {
	return generation.transportConfigurationDigest
}

// OwnerReceipt returns a defensive copy of the deterministic owner receipt.
func (generation Generation) OwnerReceipt() []byte {
	return cloneBinary(generation.ownerReceipt)
}

// OwnerReceiptByteCount returns the exact retained byte count without copying
// the immutable owner receipt.
func (generation Generation) OwnerReceiptByteCount() uint64 {
	return uint64(len(generation.ownerReceipt))
}

// OwnerReceiptDigest returns the SHA-256 digest of the exact receipt bytes.
func (generation Generation) OwnerReceiptDigest() string { return generation.ownerReceiptDigest }

func (generation Generation) copy() Generation {
	generation.transportConfiguration = cloneBinary(generation.transportConfiguration)
	generation.ownerReceipt = cloneBinary(generation.ownerReceipt)
	return generation
}

// cloneBinary always returns an owned, non-nil slice. A zero-length transport
// configuration is a valid exact byte string, but database/sql treats a nil
// []byte as SQL NULL; normalizing both nil and empty inputs to an allocated
// zero-length slice preserves that value for the NOT NULL BLOB column.
func cloneBinary(value []byte) []byte {
	result := make([]byte, len(value))
	copy(result, value)
	return result
}

func (generation Generation) equal(other Generation) bool {
	return generation.receiptProtocolVersion == other.receiptProtocolVersion &&
		generation.id == other.id &&
		generation.stableCredentialIdentity == other.stableCredentialIdentity &&
		generation.providerScopeID == other.providerScopeID &&
		generation.connectorAccountID == other.connectorAccountID &&
		generation.secretBackendProtocol == other.secretBackendProtocol &&
		generation.secretBackendVersionRef == other.secretBackendVersionRef &&
		bytes.Equal(generation.transportConfiguration, other.transportConfiguration) &&
		generation.transportConfigurationDigest == other.transportConfigurationDigest &&
		bytes.Equal(generation.ownerReceipt, other.ownerReceipt) &&
		generation.ownerReceiptDigest == other.ownerReceiptDigest
}
