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

/// Renders the Backlog board screen shell for the FocusFlow Flutter UI.
library;

import 'dart:async';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:scheduler_core/scheduler_core.dart';

import '../../controllers/backlog_board_controller.dart';
import '../../controllers/scheduler_command_controller.dart';
import '../../models/backlog/backlog_board_screen_data.dart';
import '../../theme/focus_flow_tokens.dart';
import 'backlog_board_column.dart';
import 'backlog_board_controls.dart';
import 'backlog_summary_panel.dart';
import 'backlog_task_drawer.dart';

/// Backlog board screen backed by a public scheduler read controller.
class BacklogBoardScreen extends StatelessWidget {
  /// Creates a Backlog board screen.
  const BacklogBoardScreen({
    required this.controller,
    this.commandController,
    super.key,
  });

  /// Controller that owns Backlog board read and selection state.
  final BacklogBoardController controller;

  /// Optional command controller used by Backlog actions.
  final SchedulerCommandController? commandController;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _screenAnimation(controller, commandController),
      builder: (context, _) {
        final commandState =
            commandController?.state ?? const SchedulerCommandIdle();
        void scheduleSelectedTask() {
          final detail = controller.selectedTaskDetail;
          if (detail == null) {
            return;
          }
          unawaited(
            _scheduleSelectedBacklogTask(
              detail: detail,
              boardController: controller,
              commandController: commandController,
            ),
          );
        }

        void breakUpSelectedTask() {
          final detail = controller.selectedTaskDetail;
          if (detail == null) {
            return;
          }
          unawaited(
            _breakUpSelectedBacklogTask(
              context: context,
              detail: detail,
              boardController: controller,
              commandController: commandController,
            ),
          );
        }

        void pushSelectedTaskToSomeday() {
          final detail = controller.selectedTaskDetail;
          if (detail == null) {
            return;
          }
          unawaited(
            _pushSelectedBacklogTaskToSomeday(
              detail: detail,
              boardController: controller,
              commandController: commandController,
            ),
          );
        }

        void removeSelectedTask() {
          final detail = controller.selectedTaskDetail;
          if (detail == null) {
            return;
          }
          unawaited(
            _removeSelectedBacklogTask(
              context: context,
              detail: detail,
              boardController: controller,
              commandController: commandController,
            ),
          );
        }

        return switch (controller.state) {
          BacklogBoardLoading() => _BacklogFrame(
            taskCountLabel: '...',
            controller: controller,
            commandState: commandState,
            onNewTask: _newTaskHandler(context, commandController),
            body: const _BacklogLoadingBody(),
          ),
          BacklogBoardFailure(:final message) => _BacklogFrame(
            taskCountLabel: '...',
            controller: controller,
            commandState: commandState,
            onNewTask: _newTaskHandler(context, commandController),
            body: _BacklogFailureBody(
              message: message,
              onRetry: controller.retry,
            ),
          ),
          BacklogBoardEmpty(:final data) => _BacklogFrame(
            taskCountLabel: '${data.summary.totalTaskCount} tasks',
            controller: controller,
            commandState: commandState,
            onNewTask: _newTaskHandler(context, commandController),
            body: _BacklogReadyBody(
              data: data,
              selectedTaskId: controller.selectedTaskId,
              selectedTaskDetail: controller.selectedTaskDetail,
              isDetailLoading: controller.isSelectedTaskDetailLoading,
              detailErrorMessage: controller.selectedTaskDetailError,
              detailActionMessage: controller.selectedTaskActionMessage,
              onTaskSelected: (taskId) {
                unawaited(controller.selectTask(taskId));
              },
              onCloseDrawer: controller.clearSelection,
              onAddTask: _addTaskHandler(context, commandController),
              onSchedule: scheduleSelectedTask,
              onBreakUp: breakUpSelectedTask,
              onPushToSomeday: pushSelectedTaskToSomeday,
              onRemove: removeSelectedTask,
              isEmpty: true,
            ),
          ),
          BacklogBoardReady(:final data) => _BacklogFrame(
            taskCountLabel: '${data.summary.totalTaskCount} tasks',
            controller: controller,
            commandState: commandState,
            onNewTask: _newTaskHandler(context, commandController),
            body: _BacklogReadyBody(
              data: data,
              selectedTaskId: controller.selectedTaskId,
              selectedTaskDetail: controller.selectedTaskDetail,
              isDetailLoading: controller.isSelectedTaskDetailLoading,
              detailErrorMessage: controller.selectedTaskDetailError,
              detailActionMessage: controller.selectedTaskActionMessage,
              onTaskSelected: (taskId) {
                unawaited(controller.selectTask(taskId));
              },
              onCloseDrawer: controller.clearSelection,
              onAddTask: _addTaskHandler(context, commandController),
              onSchedule: scheduleSelectedTask,
              onBreakUp: breakUpSelectedTask,
              onPushToSomeday: pushSelectedTaskToSomeday,
              onRemove: removeSelectedTask,
            ),
          ),
        };
      },
    );
  }
}

/// Handles deferred metadata drawer action presses without mutating scheduler data.
void _handleDrawerActionPlaceholder() {}

/// Builds the listenable used to refresh Backlog UI for reads and commands.
Listenable _screenAnimation(
  BacklogBoardController controller,
  SchedulerCommandController? commandController,
) {
  final commands = commandController;
  if (commands == null) {
    return controller;
  }
  return Listenable.merge([controller, commands]);
}

/// Creates the New Task handler for the current command controller.
VoidCallback? _newTaskHandler(
  BuildContext context,
  SchedulerCommandController? commandController,
) {
  final commands = commandController;
  if (commands == null) {
    return null;
  }
  return () {
    unawaited(_showNewBacklogTaskDialog(context, commands));
  };
}

/// Creates the Add Task handler shared by Backlog columns.
BacklogBucketAddRequested _addTaskHandler(
  BuildContext context,
  SchedulerCommandController? commandController,
) {
  return (bucket) {
    final commands = commandController;
    if (commands == null) {
      return;
    }
    unawaited(_showNewBacklogTaskDialog(context, commands));
  };
}

/// Opens a minimal title-first Backlog task creation dialog.
Future<void> _showNewBacklogTaskDialog(
  BuildContext context,
  SchedulerCommandController commandController,
) async {
  final submittedTitle = await showDialog<String>(
    context: context,
    builder: (dialogContext) {
      return const _NewBacklogTaskDialog();
    },
  );
  if (submittedTitle != null) {
    await commandController.quickCaptureToBacklog(submittedTitle);
  }
}

/// Opens the Break Up dialog and persists child drafts through the command path.
Future<void> _breakUpSelectedBacklogTask({
  required BuildContext context,
  required BacklogTaskDetailReadModel detail,
  required BacklogBoardController boardController,
  required SchedulerCommandController? commandController,
}) async {
  final commands = commandController;
  if (commands == null) {
    return;
  }
  final drafts = await _showBreakUpTaskDialog(context, detail);
  if (drafts == null) {
    return;
  }
  await commands.breakUpBacklogTask(
    parentTaskId: detail.item.taskId,
    children: drafts,
    expectedUpdatedAt: detail.item.updatedAt,
  );
  if (commands.state is! SchedulerCommandFailure) {
    await boardController.refreshSelectedTaskDetail();
  }
}

/// Moves the selected Backlog task to Someday through the command path.
Future<void> _pushSelectedBacklogTaskToSomeday({
  required BacklogTaskDetailReadModel detail,
  required BacklogBoardController boardController,
  required SchedulerCommandController? commandController,
}) async {
  final commands = commandController;
  if (commands == null) {
    return;
  }
  await commands.pushBacklogTaskToSomeday(
    taskId: detail.item.taskId,
    expectedUpdatedAt: detail.item.updatedAt,
  );
  if (commands.state is! SchedulerCommandFailure) {
    await boardController.refreshSelectedTaskDetail();
  }
}

/// Confirms and archives the selected Backlog task through the command path.
Future<void> _removeSelectedBacklogTask({
  required BuildContext context,
  required BacklogTaskDetailReadModel detail,
  required BacklogBoardController boardController,
  required SchedulerCommandController? commandController,
}) async {
  final commands = commandController;
  if (commands == null) {
    return;
  }
  final confirmed = await showDialog<bool>(
    context: context,
    builder: (dialogContext) {
      return AlertDialog(
        backgroundColor: FocusFlowTokens.panelBackground,
        title: const Text('Remove from Backlog?'),
        content: const Text(
          'This hides the task from active planning. It will not schedule anything.',
        ),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.of(dialogContext).pop(false);
            },
            child: const Text('Cancel'),
          ),
          FilledButton.icon(
            key: const ValueKey('backlog-remove-confirm-button'),
            onPressed: () {
              Navigator.of(dialogContext).pop(true);
            },
            icon: const Icon(Icons.archive_outlined),
            label: const Text('Remove'),
          ),
        ],
      );
    },
  );
  if (confirmed != true) {
    return;
  }
  await commands.archiveBacklogTask(
    taskId: detail.item.taskId,
    expectedUpdatedAt: detail.item.updatedAt,
  );
  if (commands.state is SchedulerCommandSuccess) {
    boardController.clearSelection();
  }
}

/// Opens the child-entry dialog used by the Break Up action.
Future<List<ApplicationChildTaskDraft>?> _showBreakUpTaskDialog(
  BuildContext context,
  BacklogTaskDetailReadModel detail,
) {
  return showDialog<List<ApplicationChildTaskDraft>>(
    context: context,
    builder: (dialogContext) {
      return _BreakUpTaskDialog(detail: detail);
    },
  );
}

/// Dialog that collects child task drafts for a Backlog task.
class _BreakUpTaskDialog extends StatefulWidget {
  /// Creates a child-entry dialog for [detail].
  const _BreakUpTaskDialog({required this.detail});

  /// Parent task detail used for dialog title and default context.
  final BacklogTaskDetailReadModel detail;

  /// Creates the mutable state object used by this stateful widget.
  /// Flutter calls this once for each mounted widget instance before lifecycle callbacks and builds begin.
  @override
  State<_BreakUpTaskDialog> createState() => _BreakUpTaskDialogState();
}

/// State for the Break Up child-entry dialog.
class _BreakUpTaskDialogState extends State<_BreakUpTaskDialog> {
  /// Text controllers for child task titles.
  final List<TextEditingController> _titleControllers = [];

  /// Text controllers for optional child duration estimates.
  final List<TextEditingController> _durationControllers = [];

  /// Dialog-level validation message shown above the child rows.
  String? _errorText;

  /// Initializes the dialog with the two child rows required by the V1 flow.
  @override
  void initState() {
    super.initState();
    _addRow();
    _addRow();
  }

  /// Releases all child-row text controllers.
  @override
  void dispose() {
    for (final controller in _titleControllers) {
      controller.dispose();
    }
    for (final controller in _durationControllers) {
      controller.dispose();
    }
    super.dispose();
  }

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      backgroundColor: FocusFlowTokens.panelBackground,
      title: const Text('Break up task'),
      content: SizedBox(
        width: 520,
        child: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Text(
                widget.detail.item.title,
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
                style: const TextStyle(
                  color: FocusFlowTokens.textPrimary,
                  fontWeight: FontWeight.w800,
                ),
              ),
              const SizedBox(height: 12),
              if (_errorText != null) ...[
                _BreakUpDialogError(message: _errorText!),
                const SizedBox(height: 12),
              ],
              for (var index = 0; index < _titleControllers.length; index += 1)
                Padding(
                  padding: EdgeInsets.only(
                    bottom: index == _titleControllers.length - 1 ? 0 : 10,
                  ),
                  child: _BreakUpChildRow(
                    index: index,
                    titleController: _titleControllers[index],
                    durationController: _durationControllers[index],
                    canRemove: _titleControllers.length > 2,
                    onRemove: () {
                      _removeRow(index);
                    },
                  ),
                ),
              const SizedBox(height: 14),
              Align(
                alignment: Alignment.centerLeft,
                child: TextButton.icon(
                  key: const ValueKey('backlog-break-up-add-row-button'),
                  onPressed: _addEditableRow,
                  icon: const Icon(Icons.add),
                  label: const Text('Add step'),
                ),
              ),
            ],
          ),
        ),
      ),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: const Text('Cancel'),
        ),
        FilledButton.icon(
          key: const ValueKey('backlog-break-up-save-button'),
          onPressed: _submit,
          icon: const Icon(Icons.call_split),
          label: const Text('Save steps'),
        ),
      ],
    );
  }

  /// Adds a row and rebuilds after the dialog is already mounted.
  void _addEditableRow() {
    setState(_addRow);
  }

  /// Adds one child input row without triggering a rebuild by itself.
  void _addRow() {
    _titleControllers.add(TextEditingController());
    _durationControllers.add(TextEditingController());
  }

  /// Removes the child input row at [index].
  void _removeRow(int index) {
    final title = _titleControllers.removeAt(index);
    final duration = _durationControllers.removeAt(index);
    title.dispose();
    duration.dispose();
    setState(() {
      _errorText = null;
    });
  }

  /// Validates child rows and closes the dialog with typed drafts.
  void _submit() {
    final drafts = <ApplicationChildTaskDraft>[];
    for (var index = 0; index < _titleControllers.length; index += 1) {
      final title = _titleControllers[index].text.trim();
      final durationText = _durationControllers[index].text.trim();
      if (title.isEmpty && durationText.isEmpty) {
        continue;
      }
      if (title.isEmpty) {
        _showError('Add a title for each step with an estimate.');
        return;
      }
      final duration = _parseDuration(durationText);
      if (durationText.isNotEmpty && duration == null) {
        _showError('Use whole minutes for estimates.');
        return;
      }
      drafts.add(
        ApplicationChildTaskDraft(title: title, durationMinutes: duration),
      );
    }
    if (drafts.length < 2) {
      _showError('Add at least two titled steps.');
      return;
    }
    Navigator.of(
      context,
    ).pop(List<ApplicationChildTaskDraft>.unmodifiable(drafts));
  }

  /// Shows [message] as the current dialog validation error.
  void _showError(String message) {
    setState(() {
      _errorText = message;
    });
  }

  /// Parses a positive whole-minute duration from [value].
  int? _parseDuration(String value) {
    if (value.isEmpty) {
      return null;
    }
    final parsed = int.tryParse(value);
    if (parsed == null || parsed <= 0) {
      return null;
    }
    return parsed;
  }
}

/// Error panel for Break Up dialog validation.
class _BreakUpDialogError extends StatelessWidget {
  /// Creates an error panel with [message].
  const _BreakUpDialogError({required this.message});

  /// Validation message to render.
  final String message;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BoxDecoration(
        color: FocusFlowTokens.warningYellow.withValues(alpha: 0.1),
        borderRadius: BorderRadius.circular(8),
        border: Border.all(
          color: FocusFlowTokens.warningYellow.withValues(alpha: 0.42),
        ),
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9),
        child: Row(
          children: [
            const Icon(
              Icons.info_outline,
              color: FocusFlowTokens.warningYellow,
              size: 18,
            ),
            const SizedBox(width: 9),
            Expanded(
              child: Text(
                message,
                style: const TextStyle(
                  color: FocusFlowTokens.textPrimary,
                  fontSize: 12.5,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/// One editable child row in the Break Up dialog.
class _BreakUpChildRow extends StatelessWidget {
  /// Creates a child row for [index].
  const _BreakUpChildRow({
    required this.index,
    required this.titleController,
    required this.durationController,
    required this.canRemove,
    required this.onRemove,
  });

  /// Zero-based row index used for stable widget keys and labels.
  final int index;

  /// Text controller for the child title.
  final TextEditingController titleController;

  /// Text controller for optional duration minutes.
  final TextEditingController durationController;

  /// Whether the remove button should be enabled for this row.
  final bool canRemove;

  /// Callback that removes this row.
  final VoidCallback onRemove;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    final rowNumber = index + 1;
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Expanded(
          child: TextField(
            key: ValueKey('backlog-break-up-title-field-$index'),
            controller: titleController,
            textInputAction: TextInputAction.next,
            style: const TextStyle(color: FocusFlowTokens.textPrimary),
            decoration: InputDecoration(
              labelText: 'Step $rowNumber',
              hintText: 'Child task title',
            ),
          ),
        ),
        const SizedBox(width: 10),
        SizedBox(
          width: 110,
          child: TextField(
            key: ValueKey('backlog-break-up-duration-field-$index'),
            controller: durationController,
            keyboardType: TextInputType.number,
            textInputAction: TextInputAction.next,
            style: const TextStyle(color: FocusFlowTokens.textPrimary),
            decoration: const InputDecoration(
              labelText: 'Minutes',
              hintText: 'Optional',
            ),
          ),
        ),
        const SizedBox(width: 6),
        IconButton(
          key: ValueKey('backlog-break-up-remove-row-button-$index'),
          tooltip: 'Remove step',
          onPressed: canRemove ? onRemove : null,
          icon: const Icon(Icons.close),
        ),
      ],
    );
  }
}

/// Dialog that collects the title for a new Backlog task.
class _NewBacklogTaskDialog extends StatefulWidget {
  /// Creates a new Backlog task dialog.
  const _NewBacklogTaskDialog();

  /// Creates the mutable state object used by this stateful widget.
  /// Flutter calls this once for each mounted widget instance before lifecycle callbacks and builds begin.
  @override
  State<_NewBacklogTaskDialog> createState() => _NewBacklogTaskDialogState();
}

/// State for the new Backlog task dialog.
class _NewBacklogTaskDialogState extends State<_NewBacklogTaskDialog> {
  /// Text controller for the required task title.
  late final TextEditingController _titleController;

  /// Inline validation text for the title field.
  String? _errorText;

  /// Initializes title input state before the dialog first builds.
  @override
  void initState() {
    super.initState();
    _titleController = TextEditingController();
  }

  /// Releases the dialog-owned text controller.
  @override
  void dispose() {
    _titleController.dispose();
    super.dispose();
  }

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      backgroundColor: FocusFlowTokens.panelBackground,
      title: const Text('New Backlog task'),
      content: SizedBox(
        width: 360,
        child: TextField(
          key: const ValueKey('backlog-new-task-title-field'),
          controller: _titleController,
          autofocus: true,
          textInputAction: TextInputAction.done,
          onSubmitted: (_) {
            _submit();
          },
          style: const TextStyle(color: FocusFlowTokens.textPrimary),
          decoration: InputDecoration(
            labelText: 'Title',
            errorText: _errorText,
          ),
        ),
      ),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: const Text('Cancel'),
        ),
        FilledButton.icon(
          key: const ValueKey('backlog-new-task-create-button'),
          onPressed: _submit,
          icon: const Icon(Icons.add),
          label: const Text('Create'),
        ),
      ],
    );
  }

  /// Validates and returns the entered title to the dialog caller.
  void _submit() {
    final title = _titleController.text.trim();
    if (title.isEmpty) {
      setState(() {
        _errorText = 'Title is required.';
      });
      return;
    }
    Navigator.of(context).pop(title);
  }
}

/// Schedules the selected Backlog [detail] through the application command path.
Future<void> _scheduleSelectedBacklogTask({
  required BacklogTaskDetailReadModel detail,
  required BacklogBoardController boardController,
  required SchedulerCommandController? commandController,
}) async {
  final commands = commandController;
  if (commands == null) {
    return;
  }
  final durationMinutes = detail.item.durationMinutes;
  if (durationMinutes == null) {
    boardController.showSelectedTaskActionMessage(
      'Add an estimate before scheduling.',
    );
    return;
  }
  await commands.scheduleBacklogItem(
    taskId: detail.item.taskId,
    durationMinutes: durationMinutes,
    expectedUpdatedAt: detail.item.updatedAt,
  );
  if (commands.state is SchedulerCommandSuccess) {
    boardController.clearSelection();
  }
}

/// Main Backlog frame containing header, controls, body, and overlay slot.
class _BacklogFrame extends StatelessWidget {
  /// Creates the Backlog frame.
  const _BacklogFrame({
    required this.taskCountLabel,
    required this.controller,
    required this.commandState,
    required this.onNewTask,
    required this.body,
  });

  /// Count label displayed beside the page title.
  final String taskCountLabel;

  /// Controller used by search and filter controls.
  final BacklogBoardController controller;

  /// Current scheduler command state.
  final SchedulerCommandState commandState;

  /// Callback for New Task, or null when commands are unavailable.
  final VoidCallback? onNewTask;

  /// Body rendered below the header controls.
  final Widget body;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(28, 26, 24, 28),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              _BacklogHeader(taskCountLabel: taskCountLabel),
              const SizedBox(height: 22),
              BacklogBoardTopControls(
                controller: controller,
                onNewTask: onNewTask,
              ),
              if (commandState is! SchedulerCommandIdle) ...[
                const SizedBox(height: 10),
                _BacklogCommandStatus(state: commandState),
              ],
              const SizedBox(height: 18),
              Expanded(child: body),
            ],
          ),
        ),
        const Positioned.fill(child: IgnorePointer(child: SizedBox.shrink())),
      ],
    );
  }
}

/// Compact command state banner for Backlog actions.
class _BacklogCommandStatus extends StatelessWidget {
  /// Creates a Backlog command status banner.
  const _BacklogCommandStatus({required this.state});

  /// Current scheduler command state.
  final SchedulerCommandState state;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    final (icon, label, color) = switch (state) {
      SchedulerCommandRunning(:final label) => (
        Icons.hourglass_top,
        label,
        FocusFlowTokens.warningYellow,
      ),
      SchedulerCommandSuccess(:final message) => (
        Icons.check_circle_outline,
        message,
        FocusFlowTokens.flexibleGreen,
      ),
      SchedulerCommandFailure(:final codeLabel) => (
        Icons.info_outline,
        'Could not complete action: $codeLabel',
        FocusFlowTokens.warningYellow,
      ),
      SchedulerCommandIdle() => (
        Icons.check_circle_outline,
        '',
        FocusFlowTokens.textMuted,
      ),
    };
    if (state is SchedulerCommandIdle) {
      return const SizedBox.shrink();
    }
    return DecoratedBox(
      decoration: BoxDecoration(
        color: color.withValues(alpha: 0.1),
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: color.withValues(alpha: 0.34)),
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9),
        child: Row(
          children: [
            Icon(icon, color: color, size: 17),
            const SizedBox(width: 8),
            Expanded(
              child: Text(
                label,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
                style: const TextStyle(
                  color: FocusFlowTokens.textPrimary,
                  fontSize: 12.5,
                  fontWeight: FontWeight.w700,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/// Header row for the Backlog board shell.
class _BacklogHeader extends StatelessWidget {
  /// Creates the Backlog header.
  const _BacklogHeader({required this.taskCountLabel});

  /// Count label displayed beside the page title.
  final String taskCountLabel;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Wrap(
                crossAxisAlignment: WrapCrossAlignment.center,
                spacing: 12,
                runSpacing: 8,
                children: [
                  Text(
                    'Backlog',
                    style: Theme.of(context).textTheme.displaySmall?.copyWith(
                      color: FocusFlowTokens.textPrimary,
                      fontSize: 34,
                      fontWeight: FontWeight.w800,
                    ),
                  ),
                  _CountPill(label: taskCountLabel),
                ],
              ),
              const SizedBox(height: 10),
              Text(
                'Choose work that fits your current energy.',
                style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                  color: FocusFlowTokens.textMuted,
                ),
              ),
            ],
          ),
        ),
        const SizedBox(width: 20),
        const BacklogModeAndSettingsControls(),
      ],
    );
  }
}

/// Body shown while the Backlog board loads.
class _BacklogLoadingBody extends StatelessWidget {
  /// Creates a loading body.
  const _BacklogLoadingBody();

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return const _BacklogStatePanel(
      icon: Icons.hourglass_top,
      title: 'Loading backlog',
      message: 'Preparing your board.',
    );
  }
}

/// Body shown when the Backlog board fails to load.
class _BacklogFailureBody extends StatelessWidget {
  /// Creates a failure body.
  const _BacklogFailureBody({required this.message, required this.onRetry});

  /// Failure message or code.
  final String message;

  /// Retry callback.
  final Future<void> Function() onRetry;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return _BacklogStatePanel(
      icon: Icons.refresh,
      title: 'Could not load backlog',
      message: 'Status: $message',
      action: OutlinedButton.icon(
        onPressed: () {
          unawaited(onRetry());
        },
        icon: const Icon(Icons.refresh),
        label: const Text('Retry'),
      ),
    );
  }
}

/// Ready or empty Backlog board body.
class _BacklogReadyBody extends StatelessWidget {
  /// Creates a ready body.
  const _BacklogReadyBody({
    required this.data,
    required this.selectedTaskId,
    required this.selectedTaskDetail,
    required this.isDetailLoading,
    required this.detailErrorMessage,
    required this.detailActionMessage,
    required this.onTaskSelected,
    required this.onCloseDrawer,
    required this.onAddTask,
    required this.onSchedule,
    required this.onBreakUp,
    required this.onPushToSomeday,
    required this.onRemove,
    this.isEmpty = false,
  });

  /// Backlog board data.
  final BacklogBoardScreenData data;

  /// Currently selected task id, if any.
  final String? selectedTaskId;

  /// Loaded selected task detail, if any.
  final BacklogTaskDetailReadModel? selectedTaskDetail;

  /// Whether the selected task drawer is waiting for detail data.
  final bool isDetailLoading;

  /// Drawer-local detail error message.
  final String? detailErrorMessage;

  /// Drawer-local selected-task action message.
  final String? detailActionMessage;

  /// Callback invoked when a task card is selected.
  final BacklogTaskSelected onTaskSelected;

  /// Callback invoked when the drawer closes.
  final VoidCallback onCloseDrawer;

  /// Callback invoked when a column add-task control is pressed.
  final BacklogBucketAddRequested onAddTask;

  /// Callback invoked when the drawer Schedule action is pressed.
  final VoidCallback onSchedule;

  /// Callback invoked when the drawer Break Up action is pressed.
  final VoidCallback onBreakUp;

  /// Callback invoked when the drawer Push to Someday action is pressed.
  final VoidCallback onPushToSomeday;

  /// Callback invoked when the drawer Remove action is pressed.
  final VoidCallback onRemove;

  /// Whether the board has no tasks.
  final bool isEmpty;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        return Stack(
          fit: StackFit.expand,
          children: [
            Row(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Expanded(
                  child: _BacklogBoardColumns(
                    columns: data.columns,
                    selectedTaskId: selectedTaskId,
                    onTaskSelected: onTaskSelected,
                    onAddTask: onAddTask,
                  ),
                ),
                const SizedBox(width: 16),
                SizedBox(
                  width: 292,
                  child: isEmpty
                      ? const _BacklogStatePanel(
                          icon: Icons.check_circle_outline,
                          title: 'Backlog is clear',
                          message: 'New tasks will appear here.',
                        )
                      : BacklogSummaryPanel(summary: data.summary),
                ),
              ],
            ),
            if (selectedTaskId != null)
              Positioned(
                top: 0,
                right: 0,
                bottom: 0,
                width: _drawerWidthFor(constraints.maxWidth),
                child: BacklogTaskDrawer(
                  key: const ValueKey('backlog-task-drawer'),
                  selectedTaskId: selectedTaskId,
                  detail: selectedTaskDetail,
                  loading: isDetailLoading,
                  errorMessage: detailErrorMessage,
                  actionMessage: detailActionMessage,
                  onClose: onCloseDrawer,
                  onSchedule: onSchedule,
                  onBreakUp: onBreakUp,
                  onPushToSomeday: onPushToSomeday,
                  onRemove: onRemove,
                  onProjectPressed: _handleDrawerActionPlaceholder,
                  onAddTagPressed: _handleDrawerActionPlaceholder,
                  onViewDayPressed: _handleDrawerActionPlaceholder,
                ),
              ),
          ],
        );
      },
    );
  }
}

/// Calculates the drawer width for [availableWidth] while leaving board context visible.
double _drawerWidthFor(double availableWidth) {
  const preferredWidth = 466.0;
  const minimumDrawerWidth = 320.0;
  const safeVisibleBoardWidth = 360.0;
  if (!availableWidth.isFinite) {
    return preferredWidth;
  }
  final widthAllowedByBoard = math.max(
    minimumDrawerWidth,
    availableWidth - safeVisibleBoardWidth,
  );
  return math.min(preferredWidth, widthAllowedByBoard);
}

/// Horizontally scrollable set of Backlog board columns.
class _BacklogBoardColumns extends StatelessWidget {
  /// Creates a board column layout.
  const _BacklogBoardColumns({
    required this.columns,
    required this.selectedTaskId,
    required this.onTaskSelected,
    required this.onAddTask,
  });

  /// Columns to render in scheduler-provided display order.
  final List<BacklogBoardColumnReadModel> columns;

  /// Currently selected task id, if any.
  final String? selectedTaskId;

  /// Callback invoked when a task card is selected.
  final BacklogTaskSelected onTaskSelected;

  /// Callback invoked when an add-task control is pressed.
  final BacklogBucketAddRequested onAddTask;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    if (columns.isEmpty) {
      return const SizedBox.shrink();
    }
    return LayoutBuilder(
      builder: (context, constraints) {
        const columnGap = 10.0;
        const minColumnWidth = 260.0;
        const maxColumnWidth = 300.0;
        final gapWidth = columnGap * (columns.length - 1);
        final availableWidth = constraints.hasBoundedWidth
            ? constraints.maxWidth
            : maxColumnWidth * columns.length + gapWidth;
        final columnWidth = ((availableWidth - gapWidth) / columns.length)
            .clamp(minColumnWidth, maxColumnWidth)
            .toDouble();
        final boardWidth = columnWidth * columns.length + gapWidth;
        final contentWidth = boardWidth < availableWidth
            ? availableWidth
            : boardWidth;
        return SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: SizedBox(
            width: contentWidth,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                for (var index = 0; index < columns.length; index += 1) ...[
                  SizedBox(
                    width: columnWidth,
                    child: BacklogBoardColumn(
                      column: columns[index],
                      selectedTaskId: selectedTaskId,
                      onTaskSelected: onTaskSelected,
                      onAddTask: onAddTask,
                    ),
                  ),
                  if (index != columns.length - 1)
                    const SizedBox(width: columnGap),
                ],
              ],
            ),
          ),
        );
      },
    );
  }
}

/// Reusable centered state panel.
class _BacklogStatePanel extends StatelessWidget {
  /// Creates a state panel.
  const _BacklogStatePanel({
    required this.icon,
    required this.title,
    required this.message,
    this.action,
  });

  /// Leading icon.
  final IconData icon;

  /// Panel title.
  final String title;

  /// Panel message.
  final String message;

  /// Optional action widget.
  final Widget? action;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 360),
        child: DecoratedBox(
          decoration: BoxDecoration(
            color: FocusFlowTokens.panelBackground,
            borderRadius: BorderRadius.circular(8),
            border: Border.all(color: FocusFlowTokens.subtleBorder),
          ),
          child: Padding(
            padding: const EdgeInsets.all(24),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(icon, color: FocusFlowTokens.accentMagenta, size: 28),
                const SizedBox(height: 14),
                Text(
                  title,
                  textAlign: TextAlign.center,
                  style: const TextStyle(
                    color: FocusFlowTokens.textPrimary,
                    fontSize: 18,
                    fontWeight: FontWeight.w800,
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  message,
                  textAlign: TextAlign.center,
                  style: const TextStyle(color: FocusFlowTokens.textMuted),
                ),
                if (action != null) ...[const SizedBox(height: 18), action!],
              ],
            ),
          ),
        ),
      ),
    );
  }
}

/// Small count pill used by headers.
class _CountPill extends StatelessWidget {
  /// Creates a count pill.
  const _CountPill({required this.label});

  /// Pill label.
  final String label;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    const accent = FocusFlowTokens.textMuted;
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
      decoration: BoxDecoration(
        color: accent.withValues(alpha: 0.16),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Text(
        label,
        style: const TextStyle(
          color: FocusFlowTokens.textPrimary,
          fontSize: 12,
          fontWeight: FontWeight.w800,
        ),
      ),
    );
  }
}
