executeCascadeDeleteWithOptions method

Future<CascadeResult<T>> executeCascadeDeleteWithOptions(
  1. String entityId,
  2. String userId,
  3. CascadeOptions options
)

Executes cascade delete with enhanced options (dry-run, progress, etc.).

Implementation

Future<CascadeResult<T>> executeCascadeDeleteWithOptions(
  String entityId,
  String userId,
  CascadeOptions options,
) async {
  final analyticsBuilder = CascadeAnalyticsBuilder();
  analyticsBuilder.startOperation(dryRun: options.dryRun);

  try {
    _ensureInitialized();

    // Check for cancellation at the start
    if (options.cancellationToken?.isCancelled ?? false) {
      analyticsBuilder.completeOperation();
      return CascadeFailure<T>(
        entity: null,
        error: CascadeError.cancelled(),
      );
    }

    // Check for user switch before proceeding.
    await _syncEngineInstance.checkForUserSwitch(userId);

    final entity = await localAdapter.read(entityId, userId: userId);
    analyticsBuilder.recordQueryExecuted();

    if (entity == null) {
      analyticsBuilder.completeOperation();
      return CascadeFailure<T>(
        entity: null,
        error: CascadeError.entityNotFound(entityId),
      );
    }

    analyticsBuilder.recordEntityProcessed(entity.runtimeType);

    if (!entity.isRelational) {
      // Fall back to regular delete for non-relational entities
      if (options.dryRun) {
        analyticsBuilder.recordEntityDeleted(entity.runtimeType);
        analyticsBuilder.completeOperation();
        return CascadeSuccess<T>(
          entity: entity,
          totalDeleted: 1,
          deletedEntities: {
            T: [entity]
          },
          restrictedRelations: {},
          analytics: analyticsBuilder.build(),
        );
      }

      final deleted = await delete(id: entityId, userId: userId);
      if (deleted) {
        analyticsBuilder.recordEntityDeleted(entity.runtimeType);
        analyticsBuilder.completeOperation();
        return CascadeSuccess<T>(
          entity: entity,
          totalDeleted: 1,
          deletedEntities: {
            T: [entity]
          },
          restrictedRelations: {},
          analytics: analyticsBuilder.build(),
        );
      } else {
        analyticsBuilder.recordError();
        analyticsBuilder.completeOperation();
        return CascadeFailure<T>(
          entity: entity,
          error: CascadeError.deleteFailed(T.toString(), entityId, 'Delete operation failed'),
        );
      }
    }

    final deletePlan = await _buildCascadeDeletePlan(entity, userId, analyticsBuilder);

    // For dry-run, just return the plan without executing
    if (options.dryRun) {
      final deletedEntities = <Type, List<DatumEntityInterface>>{};
      var completed = 0;

      // Simulate progress for dry-run
      for (final step in deletePlan.steps) {
        deletedEntities.putIfAbsent(step.entity.runtimeType, () => []).add(step.entity);
        analyticsBuilder.recordEntityDeleted(step.entity.runtimeType);
        completed++;
        options.onProgress?.call(CascadeProgress(
          completed: completed,
          total: deletePlan.steps.length,
          currentEntityType: step.entity.runtimeType.toString(),
          currentEntityId: step.entity.id,
          message: 'Planning deletion of ${step.entity.runtimeType.toString()}',
        ));
      }

      analyticsBuilder.completeOperation();
      return CascadeSuccess<T>(
        entity: entity,
        totalDeleted: deletePlan.steps.length,
        deletedEntities: deletedEntities,
        restrictedRelations: deletePlan.restrictedRelations,
        analytics: analyticsBuilder.build(),
      );
    }

    if (!deletePlan.canDelete) {
      final restrictedEntityIds = deletePlan.restrictedRelations.values.expand((entities) => entities).map((e) => e.id).toList();
      analyticsBuilder.recordRestrictViolation();
      analyticsBuilder.completeOperation();

      return CascadeFailure<T>(
        entity: entity,
        error: CascadeError.restrictViolation(
          deletePlan.restrictedRelations.keys.first,
          restrictedEntityIds,
        ),
      );
    }

    // Execute the delete plan with progress tracking
    final result = await _executeCascadeDeletePlanWithProgress(
      deletePlan,
      userId,
      DataSource.local,
      false,
      options,
      analyticsBuilder,
    );

    // Emit the main entity delete event
    _eventController.add(
      DataChangeEvent<T>(
        userId: userId,
        data: entity,
        changeType: ChangeType.deleted,
        source: DataSource.local,
      ),
    );

    analyticsBuilder.completeOperation();

    if (result.success) {
      return CascadeSuccess<T>(
        entity: entity,
        totalDeleted: result.totalDeleted,
        deletedEntities: result.deletedEntities,
        restrictedRelations: result.restrictedRelations,
        analytics: analyticsBuilder.build(),
      );
    } else {
      analyticsBuilder.recordError();
      return CascadeFailure<T>(
        entity: entity,
        error: CascadeError.deleteFailed(T.toString(), entityId, result.errors.join(', ')),
        errors: result.errors,
      );
    }
  } catch (e) {
    analyticsBuilder.recordError();
    analyticsBuilder.completeOperation();
    rethrow;
  }
}