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

part of '../occupancy_policy.dart';

/// UI-independent V1 occupancy policy.
class OccupancyPolicy {
  /// Creates a `OccupancyPolicy` 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.
  const OccupancyPolicy();

  /// Classify all task and external interval inputs for a scheduling operation.
  List<OccupancyEntry> classify({
    required Iterable<Task> tasks,
    Iterable<TimeInterval> lockedIntervals = const <TimeInterval>[],
    Iterable<TimeInterval> requiredVisibleIntervals = const <TimeInterval>[],
  }) {
    return List<OccupancyEntry>.unmodifiable([
      ...tasks.map(classifyTask),
      ...lockedIntervals.map(classifyLockedInterval),
      ...requiredVisibleIntervals.map(classifyRequiredVisibleInterval),
    ]);
  }

  /// Classify one task record.
  OccupancyEntry classifyTask(Task task) {
    final scheduledInterval = _scheduledIntervalFor(task);
    final actualInterval = _actualIntervalFor(task);

    if (_isNonOccupyingStatus(task.status)) {
      return _taskEntry(
        task: task,
        category: OccupancyCategory.nonOccupyingRecord,
        interval: null,
      );
    }

    if (task.status == TaskStatus.completed) {
      final completedInterval = actualInterval ??
          (task.type == TaskType.surprise ? scheduledInterval : null);

      if (completedInterval == null) {
        return _taskEntry(
          task: task,
          category: OccupancyCategory.nonOccupyingRecord,
          interval: null,
        );
      }

      return _taskEntry(
        task: task,
        category: OccupancyCategory.completedActualOccupancy,
        interval: completedInterval,
      );
    }

    if (task.status == TaskStatus.missed) {
      if (scheduledInterval == null) {
        return _taskEntry(
          task: task,
          category: OccupancyCategory.nonOccupyingRecord,
          interval: null,
        );
      }

      return _taskEntry(
        task: task,
        category: OccupancyCategory.retainedMissedHistoricalInterval,
        interval: scheduledInterval,
      );
    }

    if (task.type == TaskType.locked) {
      return _taskEntry(
        task: task,
        category: OccupancyCategory.hiddenLockedConstraint,
        interval: scheduledInterval,
      );
    }

    if (task.type == TaskType.freeSlot) {
      return _taskEntry(
        task: task,
        category: OccupancyCategory.protectedFreeSlot,
        interval: scheduledInterval,
      );
    }

    if (task.status == TaskStatus.active) {
      return _taskEntry(
        task: task,
        category: OccupancyCategory.activeWork,
        interval: actualInterval ?? scheduledInterval,
      );
    }

    if (task.isRequiredVisible) {
      return _taskEntry(
        task: task,
        category: OccupancyCategory.requiredVisibleCommitment,
        interval: scheduledInterval,
      );
    }

    if (task.isFlexible && task.status == TaskStatus.planned) {
      return _taskEntry(
        task: task,
        category: OccupancyCategory.movablePlannedFlexible,
        interval: scheduledInterval,
      );
    }

    // Surprise tasks should normally be completed records. If an unsupported
    // lifecycle combination appears, keep it out of automatic placement instead
    // of inventing movement behavior for it.
    return _taskEntry(
      task: task,
      category: OccupancyCategory.nonOccupyingRecord,
      interval: null,
    );
  }

  /// Classify one hidden locked interval supplied outside the task list.
  OccupancyEntry classifyLockedInterval(TimeInterval interval) {
    return _entry(
      source: OccupancySource.lockedInterval,
      category: OccupancyCategory.hiddenLockedConstraint,
      interval: interval,
      task: null,
    );
  }

  /// Classify one visible required interval supplied outside the task list.
  OccupancyEntry classifyRequiredVisibleInterval(TimeInterval interval) {
    return _entry(
      source: OccupancySource.requiredVisibleInterval,
      category: OccupancyCategory.requiredVisibleCommitment,
      interval: interval,
      task: null,
    );
  }

  /// Runs the `_taskEntry` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  OccupancyEntry _taskEntry({
    required Task task,
    required OccupancyCategory category,
    required TimeInterval? interval,
  }) {
    return _entry(
      source: OccupancySource.task,
      category: category,
      interval: interval,
      task: task,
    );
  }

  /// Runs the `_entry` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  OccupancyEntry _entry({
    required OccupancySource source,
    required OccupancyCategory category,
    required TimeInterval? interval,
    required Task? task,
  }) {
    final hasInterval = interval != null;

    return switch (category) {
      OccupancyCategory.nonOccupyingRecord => OccupancyEntry(
          source: source,
          category: category,
          interval: null,
          task: task,
          isImmovable: false,
          blocksAutomaticFlexiblePlacement: false,
          mayBeExplicitlyOverlappedByRequiredCommitment: false,
          reportsConflict: false,
          hiddenByDefault: false,
        ),
      OccupancyCategory.movablePlannedFlexible => OccupancyEntry(
          source: source,
          category: category,
          interval: interval,
          task: task,
          isImmovable: false,
          blocksAutomaticFlexiblePlacement: false,
          mayBeExplicitlyOverlappedByRequiredCommitment: false,
          reportsConflict: false,
          hiddenByDefault: false,
        ),
      OccupancyCategory.protectedFreeSlot => OccupancyEntry(
          source: source,
          category: category,
          interval: interval,
          task: task,
          isImmovable: true,
          blocksAutomaticFlexiblePlacement: hasInterval,
          mayBeExplicitlyOverlappedByRequiredCommitment: hasInterval,
          reportsConflict: hasInterval,
          hiddenByDefault: false,
        ),
      OccupancyCategory.hiddenLockedConstraint => OccupancyEntry(
          source: source,
          category: category,
          interval: interval,
          task: task,
          isImmovable: true,
          blocksAutomaticFlexiblePlacement: hasInterval,
          mayBeExplicitlyOverlappedByRequiredCommitment: false,
          reportsConflict: hasInterval,
          hiddenByDefault: true,
        ),
      OccupancyCategory.requiredVisibleCommitment ||
      OccupancyCategory.activeWork ||
      OccupancyCategory.completedActualOccupancy ||
      OccupancyCategory.retainedMissedHistoricalInterval =>
        OccupancyEntry(
          source: source,
          category: category,
          interval: interval,
          task: task,
          isImmovable: true,
          blocksAutomaticFlexiblePlacement: hasInterval,
          mayBeExplicitlyOverlappedByRequiredCommitment: false,
          reportsConflict: hasInterval,
          hiddenByDefault: false,
        ),
    };
  }
}

/// Top-level helper that performs the `_isNonOccupyingStatus` 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 _isNonOccupyingStatus(TaskStatus status) {
  return status == TaskStatus.backlog ||
      status == TaskStatus.cancelled ||
      status == TaskStatus.noLongerRelevant;
}

/// Top-level helper that performs the `_scheduledIntervalFor` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
TimeInterval? _scheduledIntervalFor(Task task) {
  final start = task.scheduledStart;
  final end = task.scheduledEnd;
  if (start == null || end == null) {
    return null;
  }

  return TimeInterval(start: start, end: end, label: task.id);
}

/// Top-level helper that performs the `_actualIntervalFor` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
TimeInterval? _actualIntervalFor(Task task) {
  final start = task.actualStart;
  final end = task.actualEnd;
  if (start == null || end == null) {
    return null;
  }

  return TimeInterval(start: start, end: end, label: task.id);
}
