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

part of '../../backlog.dart';

/// Deterministic V1 policy that assigns backlog tasks to board columns.
class BacklogBoardBucketPolicy {
  /// Creates the default V1 backlog board bucket policy.
  const BacklogBoardBucketPolicy();

  /// Resolves the board bucket for [task].
  ///
  /// [hasChildTasks] is supplied by the application read layer after it has
  /// looked up child task metadata. The policy stays pure and does not read
  /// repositories directly.
  BacklogBoardBucketResolution resolve({
    required Task task,
    bool hasChildTasks = false,
  }) {
    if (task.backlogTags.contains(BacklogTag.wishlist)) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.notNow,
        reason: 'Saved for someday.',
      );
    }
    if (_isLowPriority(task.priority) &&
        !_isOtherwiseUrgent(task: task, hasChildTasks: hasChildTasks)) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.notNow,
        reason: 'Low pressure and not urgent.',
      );
    }
    if (task.durationMinutes == null &&
        !_isReadyToSchedule(task: task, hasChildTasks: hasChildTasks)) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.notNow,
        reason: 'Needs an estimate before scheduling.',
      );
    }
    if ((task.durationMinutes ?? 0) >= 90) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.breakUpFirst,
        reason: 'Large enough to break into smaller steps.',
      );
    }
    if (_isHard(task.difficulty)) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.breakUpFirst,
        reason: 'High effort; smaller steps may help.',
      );
    }
    if (hasChildTasks) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.breakUpFirst,
        reason: 'Already has child-task planning.',
      );
    }
    if ((task.durationMinutes ?? 0) >= 45) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.needTimeBlock,
        reason: 'Needs a focused time block.',
      );
    }
    if (_isHighPriority(task.priority)) {
      return const BacklogBoardBucketResolution(
        bucket: BacklogBoardBucket.needTimeBlock,
        reason: 'High priority work.',
      );
    }

    return const BacklogBoardBucketResolution(
      bucket: BacklogBoardBucket.doNext,
      reason: 'Ready for the next available slot.',
    );
  }

  /// Whether [priority] should stay out of the main queue when no urgent signal exists.
  bool _isLowPriority(PriorityLevel? priority) {
    return priority == PriorityLevel.veryLow || priority == PriorityLevel.low;
  }

  /// Whether [priority] should be treated as important enough to plan deliberately.
  bool _isHighPriority(PriorityLevel? priority) {
    return priority == PriorityLevel.high || priority == PriorityLevel.veryHigh;
  }

  /// Whether [difficulty] indicates the task should be broken down first.
  bool _isHard(DifficultyLevel difficulty) {
    return difficulty == DifficultyLevel.hard ||
        difficulty == DifficultyLevel.veryHard;
  }

  /// Whether [task] has any signal that should override low-pressure handling.
  bool _isOtherwiseUrgent({
    required Task task,
    required bool hasChildTasks,
  }) {
    return _isHighPriority(task.priority) ||
        (task.durationMinutes ?? 0) >= 45 ||
        _isHard(task.difficulty) ||
        hasChildTasks;
  }

  /// Whether [task] has enough scheduling information for board planning.
  bool _isReadyToSchedule({
    required Task task,
    required bool hasChildTasks,
  }) {
    return task.durationMinutes != null ||
        _isHighPriority(task.priority) ||
        _isHard(task.difficulty) ||
        hasChildTasks;
  }
}
