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

/// Implements Task Statistics behavior for the Scheduler Core package.
library;

// Quiet per-task history counters.
//
// The app design calls for recovery/reporting features that can notice repeated
// pushes, backlog moves, burnout skips, and locked-hour leakage. This file keeps
// that history as immutable counters attached to each task.

/// Internal counters used for future filtering, reporting, and scheduling hints.
///
/// These counters answer "what has happened to this task over time?" without
/// changing the primary task state. They are intentionally quiet metadata: daily
/// task cards should not show all of this by default, but reports and filters can
/// use it to identify patterns such as tasks repeatedly pushed, skipped during
/// burnout, or completed inside locked hours.
///
/// Like [Task], this is immutable. Increment helpers return a new
/// [TaskStatistics] value so calling code can update a task through `copyWith`
/// while retaining predictable before/after behavior.
class TaskStatistics {
  /// Creates a `TaskStatistics` 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 TaskStatistics({
    this.skippedDuringBurnoutCount = 0,
    this.manuallyPushedCount = 0,
    this.autoPushedCount = 0,
    this.movedToBacklogCount = 0,
    this.restoredFromBacklogCount = 0,
    this.missedCount = 0,
    this.cancelledCount = 0,
    this.completedLateCount = 0,
    this.completedDuringLockedHoursCount = 0,
    this.completedDuringLockedHoursMinutes = 0,
    this.completedAfterShieldCount = 0,
    this.completedAfterPushCount = 0,
    this.totalPushesBeforeCompletion = 0,
  });

  /// Number of times this task was skipped during Shield/recovery behavior.
  final int skippedDuringBurnoutCount;

  /// Number of times the user explicitly pushed this task.
  final int manuallyPushedCount;

  /// Number of times the scheduler moved this task to make room for something.
  final int autoPushedCount;

  /// Number of times this task was moved from schedule/today into backlog.
  final int movedToBacklogCount;

  /// Number of times this task came back from backlog into a planned slot.
  final int restoredFromBacklogCount;

  /// Number of times this task missed its intended timing.
  final int missedCount;

  /// Number of times this task was intentionally cancelled.
  final int cancelledCount;

  /// Number of times this task was completed after its scheduled window.
  final int completedLateCount;

  /// Number of completion events that overlapped locked hours.
  final int completedDuringLockedHoursCount;

  /// Total minutes completed while overlapping locked hours.
  final int completedDuringLockedHoursMinutes;

  /// Dormant V2-compatible counter for future Shield completion workflows.
  final int completedAfterShieldCount;

  /// Number of completions that happened after at least one push.
  final int completedAfterPushCount;

  /// Sum of push counts present when completion was recorded.
  final int totalPushesBeforeCompletion;

  /// Return a copy with selected counters changed.
  ///
  /// Counters default to their current values when omitted, which keeps small
  /// increment helpers concise and avoids direct mutation.
  TaskStatistics copyWith({
    int? skippedDuringBurnoutCount,
    int? manuallyPushedCount,
    int? autoPushedCount,
    int? movedToBacklogCount,
    int? restoredFromBacklogCount,
    int? missedCount,
    int? cancelledCount,
    int? completedLateCount,
    int? completedDuringLockedHoursCount,
    int? completedDuringLockedHoursMinutes,
    int? completedAfterShieldCount,
    int? completedAfterPushCount,
    int? totalPushesBeforeCompletion,
  }) {
    return TaskStatistics(
      skippedDuringBurnoutCount:
          skippedDuringBurnoutCount ?? this.skippedDuringBurnoutCount,
      manuallyPushedCount: manuallyPushedCount ?? this.manuallyPushedCount,
      autoPushedCount: autoPushedCount ?? this.autoPushedCount,
      movedToBacklogCount: movedToBacklogCount ?? this.movedToBacklogCount,
      restoredFromBacklogCount:
          restoredFromBacklogCount ?? this.restoredFromBacklogCount,
      missedCount: missedCount ?? this.missedCount,
      cancelledCount: cancelledCount ?? this.cancelledCount,
      completedLateCount: completedLateCount ?? this.completedLateCount,
      completedDuringLockedHoursCount: completedDuringLockedHoursCount ??
          this.completedDuringLockedHoursCount,
      completedDuringLockedHoursMinutes: completedDuringLockedHoursMinutes ??
          this.completedDuringLockedHoursMinutes,
      completedAfterShieldCount:
          completedAfterShieldCount ?? this.completedAfterShieldCount,
      completedAfterPushCount:
          completedAfterPushCount ?? this.completedAfterPushCount,
      totalPushesBeforeCompletion:
          totalPushesBeforeCompletion ?? this.totalPushesBeforeCompletion,
    );
  }

  /// Record that the task was removed from active planning and stored for later.
  TaskStatistics incrementMovedToBacklog() {
    return copyWith(movedToBacklogCount: movedToBacklogCount + 1);
  }

  /// Record a recovery/Shield skip. This is distinct from manual cancellation.
  TaskStatistics incrementSkippedDuringBurnout() {
    return copyWith(skippedDuringBurnoutCount: skippedDuringBurnoutCount + 1);
  }

  /// Record an explicit user push action.
  TaskStatistics incrementManualPush() {
    return copyWith(manuallyPushedCount: manuallyPushedCount + 1);
  }

  /// Record scheduler-driven movement caused by another placement.
  TaskStatistics incrementAutoPush() {
    return copyWith(autoPushedCount: autoPushedCount + 1);
  }

  /// Record that a backlog item was scheduled again.
  TaskStatistics incrementRestoredFromBacklog() {
    return copyWith(restoredFromBacklogCount: restoredFromBacklogCount + 1);
  }

  /// Record a missed intended time or required task handling event.
  TaskStatistics incrementMissed() {
    return copyWith(missedCount: missedCount + 1);
  }

  /// Record that the task was deliberately cancelled.
  TaskStatistics incrementCancelled() {
    return copyWith(cancelledCount: cancelledCount + 1);
  }

  /// Record that the task was completed after its planned end.
  TaskStatistics incrementCompletedLate() {
    return copyWith(completedLateCount: completedLateCount + 1);
  }

  /// Record completion that overlapped locked time by [minutes].
  ///
  /// Both count and minutes are tracked because reports may want either "how
  /// often did this happen?" or "how much time leaked into locked blocks?"
  TaskStatistics incrementCompletedDuringLockedHours(int minutes) {
    return copyWith(
      completedDuringLockedHoursCount: completedDuringLockedHoursCount + 1,
      completedDuringLockedHoursMinutes:
          completedDuringLockedHoursMinutes + minutes,
    );
  }

  /// Record that completion happened after [pushCount] prior pushes.
  TaskStatistics recordPushesBeforeCompletion(int pushCount) {
    if (pushCount <= 0) {
      return this;
    }

    return copyWith(
      completedAfterPushCount: completedAfterPushCount + 1,
      totalPushesBeforeCompletion: totalPushesBeforeCompletion + pushCount,
    );
  }
}
