package models

import (
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"strings"
)

const (
	// FlowDependencyKindIdentityConvergence identifies a dependency on one
	// cross-feed identity becoming locally resolvable. Product metadata supplies
	// the exact local selector and destination slot; the connector worker binds
	// the immutable inbound namespace/source/remote locator before persistence.
	FlowDependencyKindIdentityConvergence = "identity_convergence"

	flowIdentityConvergenceHashDomain = "nexuscrm:flow-dependency:identity-convergence:v2"
)

// FlowExecutionDependencyRequest is the direct-Go-only prerequisite emitted by
// a Flow. It cannot be persisted until the connector worker binds the immutable
// inbound namespace, source object, and remote identity.
type FlowExecutionDependencyRequest struct {
	Kind              string `json:"-"`
	ObjectAPIName     string `json:"-"`
	RecordID          string `json:"-"`
	ConnectorOwnerKey string `json:"-"`
	DestinationSlotID string `json:"-"`
}

// FlowExecutionDependency is the fully scoped durable prerequisite persisted
// by the connector engine. KeyHash binds every exact-record and connector
// identity dimension; the raw selectors must never enter sanitized summaries.
type FlowExecutionDependency struct {
	Kind                  string `json:"-"`
	ObjectAPIName         string `json:"-"`
	RecordID              string `json:"-"`
	ConnectorOwnerKey     string `json:"-"`
	ConnectorNamespace    string `json:"-"`
	ConnectorSourceObject string `json:"-"`
	DestinationSlotID     string `json:"-"`
	ConnectorRemoteID     string `json:"-"`
	KeyHash               string `json:"-"`
}

// NewFlowIdentityConvergenceDependencyRequest constructs the Flow-authored
// portion of a dependency. It performs no product-specific ID validation; L1/L2
// metadata owns that policy before invoking this neutral L0 action.
func NewFlowIdentityConvergenceDependencyRequest(
	objectAPIName,
	recordID,
	connectorOwnerKey,
	destinationSlotID string,
) (FlowExecutionDependencyRequest, error) {
	request := FlowExecutionDependencyRequest{
		Kind:              FlowDependencyKindIdentityConvergence,
		ObjectAPIName:     strings.TrimSpace(objectAPIName),
		RecordID:          strings.TrimSpace(recordID),
		ConnectorOwnerKey: strings.TrimSpace(connectorOwnerKey),
		DestinationSlotID: strings.TrimSpace(destinationSlotID),
	}
	if err := request.Validate(); err != nil {
		return FlowExecutionDependencyRequest{}, err
	}
	return request, nil
}

// Validate fails closed on an incomplete or non-canonical Flow-authored
// dependency request. A request is not a persistable dependency.
func (r FlowExecutionDependencyRequest) Validate() error {
	if r.Kind != FlowDependencyKindIdentityConvergence {
		return fmt.Errorf("unsupported flow dependency kind %q", r.Kind)
	}
	for name, value := range map[string]string{
		"object API name":     r.ObjectAPIName,
		"record ID":           r.RecordID,
		"connector owner key": r.ConnectorOwnerKey,
		"destination slot ID": r.DestinationSlotID,
	} {
		if value == "" || value != strings.TrimSpace(value) {
			return fmt.Errorf("flow identity convergence dependency request requires canonical %s", name)
		}
	}
	return nil
}

// BindFlowIdentityConvergenceDependency combines a validated Flow request with
// the immutable connector Event locator. Only the returned fully scoped value
// is eligible for WorkItem persistence, coalescing, or wakeup.
func BindFlowIdentityConvergenceDependency(
	request FlowExecutionDependencyRequest,
	connectorNamespace,
	connectorSourceObject,
	connectorRemoteID string,
) (FlowExecutionDependency, error) {
	if err := request.Validate(); err != nil {
		return FlowExecutionDependency{}, err
	}
	dependency := FlowExecutionDependency{
		Kind:                  request.Kind,
		ObjectAPIName:         request.ObjectAPIName,
		RecordID:              request.RecordID,
		ConnectorOwnerKey:     request.ConnectorOwnerKey,
		ConnectorNamespace:    strings.TrimSpace(connectorNamespace),
		ConnectorSourceObject: strings.TrimSpace(connectorSourceObject),
		DestinationSlotID:     request.DestinationSlotID,
		ConnectorRemoteID:     strings.TrimSpace(connectorRemoteID),
	}
	if dependency.ConnectorNamespace == "" || dependency.ConnectorSourceObject == "" || dependency.ConnectorRemoteID == "" {
		return FlowExecutionDependency{}, fmt.Errorf("flow identity convergence dependency requires immutable connector namespace, source object, and remote ID")
	}
	dependency.KeyHash = flowIdentityConvergenceKeyHash(dependency)
	if err := dependency.Validate(); err != nil {
		return FlowExecutionDependency{}, err
	}
	return dependency, nil
}

// flowIdentityConvergenceKeyHash derives the fixed-width lookup and
// observability key from the complete canonical dependency scope.
func flowIdentityConvergenceKeyHash(d FlowExecutionDependency) string {
	hasher := sha256.New()
	for _, value := range []string{
		flowIdentityConvergenceHashDomain,
		d.Kind,
		d.ObjectAPIName,
		d.RecordID,
		d.ConnectorOwnerKey,
		d.ConnectorNamespace,
		d.ConnectorSourceObject,
		d.DestinationSlotID,
		d.ConnectorRemoteID,
	} {
		var size [8]byte
		binary.BigEndian.PutUint64(size[:], uint64(len(value)))
		_, _ = hasher.Write(size[:])
		_, _ = hasher.Write([]byte(value))
	}
	return hex.EncodeToString(hasher.Sum(nil))
}

// Validate fails closed on incomplete, unsupported, non-canonical, or
// hash-tampered durable dependencies crossing a module boundary.
func (d FlowExecutionDependency) Validate() error {
	if d.Kind != FlowDependencyKindIdentityConvergence {
		return fmt.Errorf("unsupported flow dependency kind %q", d.Kind)
	}
	for name, value := range map[string]string{
		"object API name":         d.ObjectAPIName,
		"record ID":               d.RecordID,
		"connector owner key":     d.ConnectorOwnerKey,
		"connector namespace":     d.ConnectorNamespace,
		"connector source object": d.ConnectorSourceObject,
		"destination slot ID":     d.DestinationSlotID,
		"connector remote ID":     d.ConnectorRemoteID,
	} {
		if value == "" || value != strings.TrimSpace(value) {
			return fmt.Errorf("flow identity convergence dependency requires canonical %s", name)
		}
	}
	if d.KeyHash != flowIdentityConvergenceKeyHash(d) {
		return fmt.Errorf("flow identity convergence dependency key hash does not match its complete scope")
	}
	return nil
}
