// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only

part of '../../../scheduling_engine.dart';

/// Starter result wrapper for scheduling operations.
///
/// Every engine operation returns a [SchedulingResult], even when nothing moved.
/// This keeps the call pattern predictable: always inspect `tasks`, then surface
/// any `notices`, `changes`, or `overlaps` relevant to the UI.
class SchedulingResult {
  /// Creates a `SchedulingResult` instance with the values required by its invariants.
  /// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
  SchedulingResult({
    required List<Task> tasks,
    this.operationCode = SchedulingOperationCode.unspecified,
    this.outcomeCode = SchedulingOutcomeCode.success,
    List<SchedulingNotice> notices = const <SchedulingNotice>[],
    List<SchedulingChange> changes = const <SchedulingChange>[],
    List<SchedulingOverlap> overlaps = const <SchedulingOverlap>[],
  })  : tasks = List<Task>.unmodifiable(tasks),
        assert(
          _noticeCodesAreSinglePurpose(notices),
          'Each scheduling notice may have only one issue/movement/conflict code.',
        ),
        notices = List<SchedulingNotice>.unmodifiable(notices),
        changes = List<SchedulingChange>.unmodifiable(changes),
        overlaps = List<SchedulingOverlap>.unmodifiable(overlaps);

  /// Replacement task list after the operation.
  final List<Task> tasks;

  /// Stable operation identifier.
  final SchedulingOperationCode operationCode;

  /// Stable high-level outcome category.
  final SchedulingOutcomeCode outcomeCode;

  /// Human-readable operation messages.
  final List<SchedulingNotice> notices;

  /// Machine-readable movements or schedule clears.
  final List<SchedulingChange> changes;

  /// Analysis-only overlap findings.
  final List<SchedulingOverlap> overlaps;
}

/// Top-level helper that performs the `_noticeCodesAreSinglePurpose` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
bool _noticeCodesAreSinglePurpose(Iterable<SchedulingNotice> notices) {
  for (final notice in notices) {
    final codeCount = [
      notice.issueCode,
      notice.movementCode,
      notice.conflictCode,
    ].where((code) => code != null).length;
    if (codeCount > 1) {
      return false;
    }
  }

  return true;
}
