package persistence

import (
	"context"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"strings"
	"time"
)

// RuntimeWorkScope is the routing projection of Catalog-owned durable-work
// lineage. It is not metadata activation authority: workers still verify the
// complete immutable binding before interpreting a payload. Persisting this
// projection lets a scoped worker select only its own rows instead of claiming
// another App installation's work and poisoning it after binder rejection.
type RuntimeWorkScope struct {
	OrgID               string
	AppInstallationID   string
	RuntimeCapabilityID string
}

// Digest is the queryable identity for one AppInstallation/capability worker
// partition. The explicit columns remain recoverable authority; this bounded
// digest keeps the TiDB claim index below its key-width limit.
func (s RuntimeWorkScope) Digest() (string, error) {
	if err := s.Validate(); err != nil {
		return "", err
	}
	hasher := sha256.New()
	var size [2]byte
	for _, value := range []string{s.OrgID, s.AppInstallationID, s.RuntimeCapabilityID} {
		binary.BigEndian.PutUint16(size[:], uint16(len(value)))
		_, _ = hasher.Write(size[:])
		_, _ = hasher.Write([]byte(value))
	}
	return hex.EncodeToString(hasher.Sum(nil)), nil
}

func (s RuntimeWorkScope) Validate() error {
	for _, value := range []string{s.OrgID, s.AppInstallationID, s.RuntimeCapabilityID} {
		if value == "" || strings.TrimSpace(value) != value || len(value) > 255 {
			return errors.New("runtime work scope: org, App installation, and capability are required")
		}
	}
	return nil
}

type runtimeWorkScopeContextKey struct{}

// WithRuntimeWorkScope carries the queryable projection alongside the opaque
// immutable binding in the same foreground transaction.
func WithRuntimeWorkScope(ctx context.Context, scope RuntimeWorkScope) (context.Context, error) {
	if err := scope.Validate(); err != nil {
		return nil, err
	}
	if ctx == nil {
		ctx = context.Background()
	}
	return context.WithValue(ctx, runtimeWorkScopeContextKey{}, scope), nil
}

// RuntimeWorkScopeFromContext returns the durable-work routing projection
// installed from the row's verified immutable binding. Cross-capability
// producers use this source scope to lock the current row while deriving their
// destination scope from the separately verified target binding.
func RuntimeWorkScopeFromContext(ctx context.Context) (RuntimeWorkScope, bool) {
	if ctx == nil {
		return RuntimeWorkScope{}, false
	}
	scope, ok := ctx.Value(runtimeWorkScopeContextKey{}).(RuntimeWorkScope)
	return scope, ok && scope.Validate() == nil
}

func sameRuntimeWorkScope(left, right RuntimeWorkScope) bool {
	return left == right
}

const defaultOutboxProcessingLease = time.Duration(OutboxStaleProcessingSeconds) * time.Second
