package automation

import (
	"context"
	"fmt"
	"strings"

	apperrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/kernel/pkg/recordlocks"
	"github.com/nexuscrm/shared/pkg/models"
)

func validateFlowAtomicHistoryPrelocks(meta *models.FlowMetadata) error {
	if meta == nil {
		return nil
	}
	if err := models.ValidateFlowAtomicHistoryPrelocks(*meta); err != nil {
		return apperrors.NewValidationError("atomicHistoryPrelocks", err.Error())
	}
	return nil
}

type flowAtomicHistoryBusinessRowLocker interface {
	RunWithParentLockCoordinator(context.Context, func(context.Context) error) error
	LockParentRows(context.Context, []recordlocks.ParentKey) ([]recordlocks.ParentKey, error)
}

func flowAtomicHistoryBusinessRowObjects(meta *models.FlowMetadata) []string {
	if meta == nil {
		return nil
	}
	objects := make([]string, 0, len(meta.AtomicHistoryPrelocks))
	for _, prelock := range meta.AtomicHistoryPrelocks {
		objects = append(objects, strings.TrimSpace(prelock.Object))
	}
	return objects
}

func (e *FlowExecutor) resolveFlowAtomicHistoryPrelocks(ctx context.Context, meta *models.FlowMetadata) ([]string, error) {
	if meta == nil || len(meta.AtomicHistoryPrelocks) == 0 {
		return nil, nil
	}
	var fieldIDs []string
	for _, prelock := range meta.AtomicHistoryPrelocks {
		objectAPIName := strings.TrimSpace(prelock.Object)
		for _, declaredField := range prelock.Fields {
			fieldAPIName := strings.TrimSpace(declaredField)
			field := e.lookupFieldMeta(ctx, objectAPIName, fieldAPIName)
			if field == nil || strings.TrimSpace(field.ID) == "" {
				return nil, apperrors.NewValidationError(
					"atomicHistoryPrelocks",
					fmt.Sprintf("field %s.%s does not resolve to a stable FieldId", objectAPIName, fieldAPIName),
				)
			}
			fieldIDs = append(fieldIDs, field.ID)
		}
	}
	return ports.OrderedHistorySequenceFields(fieldIDs), nil
}

func (e *FlowExecutor) runWithFlowAtomicHistoryBusinessRows(
	ctx context.Context,
	meta *models.FlowMetadata,
	rows []recordlocks.ParentKey,
	fn func(context.Context) error,
) error {
	if meta == nil || len(meta.AtomicHistoryPrelocks) == 0 || isPreviewMode(ctx) {
		return fn(ctx)
	}
	locker, ok := e.persistence.(flowAtomicHistoryBusinessRowLocker)
	if !ok {
		return apperrors.NewInternalError("atomic history prelocks require business-row prelock support", nil)
	}
	return locker.RunWithParentLockCoordinator(ctx, func(coordinatorCtx context.Context) error {
		if len(rows) > 0 {
			if _, err := locker.LockParentRows(coordinatorCtx, rows); err != nil {
				return apperrors.NewInternalError("atomic history business-row prelock failed", err)
			}
		}
		return fn(coordinatorCtx)
	})
}
