package records

import (
	"context"
	"errors"
	"time"

	"github.com/nexuscrm/kernel/pkg/ports"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordmutationv1"
)

type runtimeRecordMutationBaseTransactions interface {
	ports.TransactionManager
	ReadTransactionTime(context.Context) (time.Time, error)
}

// runtimeRecordMutationTransactionOwner keeps command-only transactions on
// the existing transaction manager while routing transactions that contain
// Records business effects through the engine's canonical mutation boundary.
// The owner is constructed from PersistenceService so both paths necessarily
// use the same database and transaction manager.
type runtimeRecordMutationTransactionOwner struct {
	runtimeRecordMutationBaseTransactions
	persistence *PersistenceService
}

// NewRuntimeRecordMutationTransactionOwner creates the transaction dependency
// for RuntimeRecordMutationService. Passing a raw generic transaction manager
// is intentionally not sufficient: business-effect transactions must retain
// Records-owned derived-state and parent-lock invariants.
func NewRuntimeRecordMutationTransactionOwner(persistence *PersistenceService) (RuntimeRecordMutationExecutionTransactions, error) {
	if persistence == nil || persistence.txManager == nil {
		return nil, errors.New("runtime record mutation transaction owner requires persistence transactions")
	}
	transactions, ok := persistence.txManager.(runtimeRecordMutationBaseTransactions)
	if !ok {
		return nil, errors.New("runtime record mutation transaction owner requires the transaction-bound database clock")
	}
	return runtimeRecordMutationTransactionOwner{
		runtimeRecordMutationBaseTransactions: transactions,
		persistence:                           persistence,
	}, nil
}

func (o runtimeRecordMutationTransactionOwner) RunRecordMutationTransaction(
	ctx context.Context,
	objectNames []string,
	fn func(context.Context) error,
) error {
	_, err := o.persistence.RunRecordMutationTransactionWithOutcome(ctx, objectNames, fn)
	return err
}

// RunRecordMutationTransactionWithOutcome is the canonical Records-owned
// transaction boundary for callers that must atomically join durable state to
// record effects. Callers supply the complete directly-mutated object set so
// Records can install every derived-state capability before the first write.
func (ps *PersistenceService) RunRecordMutationTransactionWithOutcome(
	ctx context.Context,
	objectNames []string,
	fn func(context.Context) error,
) (ports.TransactionCompletionOutcome, error) {
	if fn == nil {
		return "", errors.New("record mutation transaction callback is required")
	}
	return ps.runGroupMembershipAwarePessimisticTransactionForObjectsWithOutcome(ctx, objectNames, fn)
}

// RecordMutationTransactionObjectNames returns the metadata-derived object set
// whose derived state may be changed directly by one mutation. Delete includes
// every metadata-declared cascade target; the other operations mutate their
// declared object directly. This keeps transaction capability selection in
// Records instead of asking a worker to duplicate relationship semantics.
func (ps *PersistenceService) RecordMutationTransactionObjectNames(
	ctx context.Context,
	objectName string,
	kind semantic.OperationKind,
) []string {
	if kind == semantic.OperationDelete {
		return ps.deleteTransactionObjectNames(ctx, objectName)
	}
	return []string{objectName}
}
