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

part of '../../task_actions.dart';

/// Applies lifecycle actions for required visible task cards.
///
/// This service only handles critical and inflexible tasks. Locked blocks are
/// scheduling constraints, not normal task cards, and flexible tasks use
/// [FlexibleTaskActionService].
class RequiredTaskActionService {
  /// Creates a `RequiredTaskActionService` 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 RequiredTaskActionService({
    this.schedulingEngine = const SchedulingEngine(),
    this.transitionService = const TaskTransitionService(),
    this.clock = const SystemClock(),
  });

  /// Scheduling dependency used for required missed behavior.
  final SchedulingEngine schedulingEngine;

  /// Canonical lifecycle transition dependency.
  final TaskTransitionService transitionService;

  /// Clock boundary used when an action timestamp is not supplied.
  final Clock clock;

  /// Apply one required-card state transition.
  RequiredTaskActionResult apply({
    required Task task,
    required RequiredTaskAction action,
    DateTime? updatedAt,
    String? operationId,
    DateTime? actualStart,
    DateTime? actualEnd,
    List<TimeInterval> lockedIntervals = const <TimeInterval>[],
    Iterable<TaskActivity> existingActivities = const <TaskActivity>[],
  }) {
    logger.debug(() => 'Applying required task action. taskId=${task.id} '
        'action=${action.name} taskType=${task.type.name} status=${task.status.name} '
        'operationId=$operationId');
    if (!task.isRequiredVisible) {
      logger.error(() => 'Required task action failed: invalid task type. '
          'taskId=${task.id} taskType=${task.type.name} action=${action.name}');
      throw ArgumentError.value(
        task.type,
        'task.type',
        'Task must be critical or inflexible.',
      );
    }

    final now = updatedAt ?? clock.now();

    final transitionCode = switch (action) {
      RequiredTaskAction.done => TaskTransitionCode.complete,
      RequiredTaskAction.missed => TaskTransitionCode.miss,
      RequiredTaskAction.cancel => TaskTransitionCode.cancel,
      RequiredTaskAction.noLongerRelevant =>
        TaskTransitionCode.noLongerRelevant,
    };
    final activityCode = switch (action) {
      RequiredTaskAction.done => TaskActivityCode.completed,
      RequiredTaskAction.missed => TaskActivityCode.missed,
      RequiredTaskAction.cancel => TaskActivityCode.cancelled,
      RequiredTaskAction.noLongerRelevant => TaskActivityCode.noLongerRelevant,
    };
    final opId = operationId ?? _defaultOperationId(action.name, task.id, now);
    logger.finer(
        () => 'Required task action resolved transition. taskId=${task.id} '
            'action=${action.name} transition=${transitionCode.name} '
            'operationId=$opId');
    final transition = transitionService.apply(
      task: task,
      transitionCode: transitionCode,
      operationId: opId,
      activityId: _activityId(opId, task.id, activityCode),
      occurredAt: now,
      actualStart: actualStart,
      actualEnd: actualEnd,
      lockedIntervals: lockedIntervals,
      existingActivities: existingActivities,
    );

    logger.debug(() => 'Required task action finished. taskId=${task.id} '
        'action=${action.name} outcome=${transition.outcomeCode.name} '
        'activityCount=${transition.activities.length} '
        'removedFromActivePlan=${transition.task.status == TaskStatus.backlog || transition.task.status == TaskStatus.cancelled || transition.task.status == TaskStatus.noLongerRelevant}');

    return RequiredTaskActionResult(
      action: action,
      task: transition.task,
      removedFromActivePlan: transition.task.status == TaskStatus.backlog ||
          transition.task.status == TaskStatus.cancelled ||
          transition.task.status == TaskStatus.noLongerRelevant,
      activities: transition.activities,
      transitionOutcomeCode: transition.outcomeCode,
    );
  }
}
