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

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

/// Window of time available to a scheduling operation.
///
/// Most engine methods operate on one planning window: "today", "tomorrow",
/// or any other bounded range supplied by the caller. The window constrains where
/// flexible tasks can be placed. Anything outside this range is treated as out of
/// scope for the operation.
class SchedulingWindow {
  /// Creates a `SchedulingWindow` 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.
  SchedulingWindow({
    required this.start,
    required this.end,
  }) {
    if (!start.isBefore(end)) {
      throw DomainValidationException(
        code: DomainValidationCode.invalidSchedulingWindow,
        invalidValue: {'start': start, 'end': end},
        name: 'SchedulingWindow',
        message: 'Scheduling window end must be after start.',
      );
    }
  }

  /// Inclusive beginning of the scheduling range.
  final DateTime start;

  /// Exclusive ending of the scheduling range.
  final DateTime end;

  /// The window as a [TimeInterval], useful for overlap checks.
  TimeInterval get interval => TimeInterval(start: start, end: end);

  /// Whether [interval] is completely inside this window.
  ///
  /// A task that starts before the window or ends after the window is considered
  /// outside the operation. The engine may treat such tasks as fixed blocks
  /// instead of moving them.
  bool contains(TimeInterval interval) {
    final startsInWindow =
        interval.start.isAfter(start) || interval.start.isAtSameMomentAs(start);
    final endsInWindow =
        interval.end.isBefore(end) || interval.end.isAtSameMomentAs(end);

    return startsInWindow && endsInWindow;
  }
}
