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

/// Tests Backlog board screen card rendering.
library;

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

import 'package:focus_flow_flutter/controllers/backlog_board_controller.dart';
import 'package:focus_flow_flutter/controllers/selected_date_controller.dart';
import 'package:focus_flow_flutter/theme/focus_flow_theme.dart';
import 'package:focus_flow_flutter/theme/focus_flow_tokens.dart';
import 'package:focus_flow_flutter/widgets/backlog/backlog_board_screen.dart';

/// Runs Backlog board widget tests.
void main() {
  testWidgets('renders card metadata child previews and selected state', (
    tester,
  ) async {
    final detailReads = <String>[];
    final item = _boardItemWithChildren();
    final board = _boardWithItem(item);
    final selectedDates = SelectedDateController(
      initialDate: CivilDate(2026, 7, 7),
    );
    final controller = BacklogBoardController(
      readBoard: (_) async => ApplicationResult.success(board),
      readTaskDetail: (taskId, localDate) async {
        detailReads.add(taskId);
        return ApplicationResult.success(_detailFor(item));
      },
      selectedDates: selectedDates,
      dateLabelFor: (date) => date.toIsoString(),
    );
    addTearDown(controller.dispose);
    addTearDown(selectedDates.dispose);

    await controller.load();
    await _pumpBacklogScreen(tester, controller);

    expect(find.text('Do Next'), findsOneWidget);
    expect(find.text('Need Time Block'), findsOneWidget);
    expect(find.text('Break Up First'), findsOneWidget);
    expect(find.text('Not Now'), findsOneWidget);
    final card = find.byKey(const ValueKey('backlog-card-plan-week'));
    expect(card, findsOneWidget);
    expect(find.text('Plan next week'), findsOneWidget);
    expect(find.text('Outline priorities + tasks'), findsOneWidget);
    expect(
      find.descendant(of: card, matching: find.text('30 min')),
      findsOneWidget,
    );
    expect(
      find.descendant(of: card, matching: find.text('Work')),
      findsOneWidget,
    );
    expect(
      find.descendant(of: card, matching: find.text('High')),
      findsOneWidget,
    );
    expect(
      find.descendant(of: card, matching: find.text('Difficulty')),
      findsOneWidget,
    );
    expect(
      find.descendant(of: card, matching: find.text('Reward')),
      findsOneWidget,
    );
    expect(
      find.byKey(const ValueKey('backlog-child-define-weekly-goals')),
      findsOneWidget,
    );
    expect(find.text('Define weekly goals'), findsOneWidget);
    expect(find.text('15 min'), findsOneWidget);

    await tester.tap(find.byKey(const ValueKey('backlog-card-plan-week')));
    await tester.pumpAndSettle();

    expect(detailReads, ['plan-week']);
    expect(controller.selectedTaskId, 'plan-week');
    final cardContainer = tester.widget<AnimatedContainer>(
      find.byKey(const ValueKey('backlog-card-container-plan-week')),
    );
    final decoration = cardContainer.decoration! as BoxDecoration;
    final border = decoration.border! as Border;
    expect(border.top.color, FocusFlowTokens.accentMagenta);
  });

  testWidgets('controls update board read requests', (tester) async {
    final requests = <BacklogBoardReadRequest>[];
    final item = _boardItemWithChildren();
    final board = _boardWithItem(
      item,
      projectOptions: const [
        ProjectOptionReadModel(
          projectId: 'work',
          name: 'Work',
          colorToken: 'project-work',
          isArchived: false,
        ),
      ],
    );
    final selectedDates = SelectedDateController(
      initialDate: CivilDate(2026, 7, 7),
    );
    final controller = BacklogBoardController(
      readBoard: (request) async {
        requests.add(request);
        return ApplicationResult.success(board);
      },
      readTaskDetail: (taskId, localDate) async {
        return ApplicationResult.success(_detailFor(item));
      },
      selectedDates: selectedDates,
      dateLabelFor: (date) => date.toIsoString(),
    );
    addTearDown(controller.dispose);
    addTearDown(selectedDates.dispose);

    await controller.load();
    await _pumpBacklogScreen(tester, controller);

    await tester.enterText(
      find.byKey(const ValueKey('backlog-search-field')),
      'course',
    );
    await tester.pumpAndSettle();

    expect(requests.last.searchText, 'course');
    expect(find.byKey(const ValueKey('backlog-search-clear')), findsOneWidget);

    await tester.tap(find.byKey(const ValueKey('backlog-search-clear')));
    await tester.pumpAndSettle();

    expect(requests.last.searchText, isNull);

    await tester.tap(find.byKey(const ValueKey('backlog-sort-menu-button')));
    await tester.pumpAndSettle();
    await tester.tap(find.text('Reward vs Effort').last);
    await tester.pumpAndSettle();

    expect(requests.last.sortKey, BacklogSortKey.rewardVsEffort);
    expect(find.text('Sort: Reward'), findsOneWidget);

    await tester.tap(find.byKey(const ValueKey('backlog-group-menu-button')));
    await tester.pumpAndSettle();
    await tester.tap(find.text('Project').last);
    await tester.pumpAndSettle();

    expect(requests.last.groupMode, BacklogBoardGroupMode.project);
    expect(find.text('Group: Project'), findsOneWidget);

    await tester.tap(find.byKey(const ValueKey('backlog-filter-button')));
    await tester.pumpAndSettle();

    Future<void> tapFilterCheckbox(String label) async {
      final checkbox = find.widgetWithText(CheckboxListTile, label);
      await tester.ensureVisible(checkbox);
      await tester.tap(checkbox);
      await tester.pump();
    }

    await tapFilterCheckbox('High priority');
    await tapFilterCheckbox('Work');
    await tapFilterCheckbox('0-30 min');
    await tapFilterCheckbox('Need Time Block');
    await tapFilterCheckbox('No reward set');
    await tester.tap(find.text('Apply'));
    await tester.pumpAndSettle();

    expect(requests.last.filters, [BacklogFilter.noRewardSet]);
    expect(
      requests.last.boardFilters.priorityGroups,
      contains(BacklogBoardPriorityFilter.high),
    );
    expect(requests.last.boardFilters.projectIds, contains('work'));
    expect(
      requests.last.boardFilters.durationBuckets,
      contains(BacklogBoardDurationFilter.zeroToThirty),
    );
    expect(
      requests.last.boardFilters.buckets,
      contains(BacklogBoardBucket.needTimeBlock),
    );
    expect(find.text('Filters 5'), findsOneWidget);

    await tester.tap(find.byKey(const ValueKey('backlog-filter-button')));
    await tester.pumpAndSettle();
    await tester.tap(find.text('Clear'));
    await tester.pumpAndSettle();

    expect(requests.last.filters, isEmpty);
    expect(requests.last.boardFilters.isEmpty, isTrue);
    expect(find.text('Filters'), findsOneWidget);
  });
}

/// Pumps [controller] inside the dark FocusFlow app shell test harness.
Future<void> _pumpBacklogScreen(
  WidgetTester tester,
  BacklogBoardController controller,
) async {
  await tester.binding.setSurfaceSize(const Size(1320, 820));
  addTearDown(() => tester.binding.setSurfaceSize(null));
  await tester.pumpWidget(
    MaterialApp(
      theme: FocusFlowTheme.dark(),
      home: Scaffold(
        body: SizedBox(
          width: 1300,
          height: 780,
          child: BacklogBoardScreen(controller: controller),
        ),
      ),
    ),
  );
  await tester.pumpAndSettle();
}

/// Builds a board item with child preview rows for widget tests.
BacklogBoardItemReadModel _boardItemWithChildren() {
  return BacklogBoardItemReadModel(
    taskId: 'plan-week',
    title: 'Plan next week',
    subtitle: 'Outline priorities + tasks',
    projectId: 'work',
    projectName: 'Work',
    projectColorToken: 'project-work',
    durationMinutes: 30,
    priority: PriorityLevel.high,
    reward: RewardLevel.high,
    difficulty: DifficultyLevel.medium,
    bucket: BacklogBoardBucket.breakUpFirst,
    bucketReason: 'Already has child-task planning.',
    createdAt: DateTime.utc(2026, 7, 7),
    updatedAt: DateTime.utc(2026, 7, 7),
    childPreview: const [
      BacklogChildPreviewReadModel(
        taskId: 'define-weekly-goals',
        title: 'Define weekly goals',
        durationMinutes: 15,
        isCompleted: false,
      ),
    ],
    tags: const ['someday'],
  );
}

/// Builds a Backlog board containing [item] and the other stable empty columns.
BacklogBoardQueryResult _boardWithItem(
  BacklogBoardItemReadModel item, {
  List<ProjectOptionReadModel> projectOptions = const [],
}) {
  return BacklogBoardQueryResult(
    ownerId: 'owner-1',
    localDate: CivilDate(2026, 7, 7),
    columns: [
      BacklogBoardColumnReadModel(
        bucket: BacklogBoardBucket.doNext,
        title: 'Do Next',
        subtitle: 'Ready when you have a small opening.',
        accentToken: 'backlog-do-next',
        items: const [],
      ),
      BacklogBoardColumnReadModel(
        bucket: BacklogBoardBucket.needTimeBlock,
        title: 'Need Time Block',
        subtitle: 'Give these a protected block.',
        accentToken: 'backlog-time-block',
        items: const [],
      ),
      BacklogBoardColumnReadModel(
        bucket: BacklogBoardBucket.breakUpFirst,
        title: 'Break Up First',
        subtitle: 'Split these before scheduling.',
        accentToken: 'backlog-break-up',
        items: [item],
      ),
      BacklogBoardColumnReadModel(
        bucket: BacklogBoardBucket.notNow,
        title: 'Not Now',
        subtitle: 'Keep these out of today pressure.',
        accentToken: 'backlog-not-now',
        items: const [],
      ),
    ],
    summary: BacklogBoardSummaryReadModel(
      totalTaskCount: 1,
      totalEstimatedMinutes: item.durationMinutes ?? 0,
      priorityDistribution: BacklogDistributionReadModel(
        title: 'By priority',
        entries: const [
          BacklogDistributionEntryReadModel(
            id: 'high',
            label: 'High',
            count: 1,
            percentage: 1,
            colorToken: 'priority-high',
          ),
        ],
      ),
      projectDistribution: BacklogDistributionReadModel(
        title: 'By project',
        entries: const [
          BacklogDistributionEntryReadModel(
            id: 'work',
            label: 'Work',
            count: 1,
            percentage: 1,
            colorToken: 'project-work',
          ),
        ],
      ),
      durationDistribution: BacklogDistributionReadModel(
        title: 'Duration distribution',
        entries: const [],
      ),
      planningTip: 'Pick one task.',
    ),
    projectOptions: projectOptions,
  );
}

/// Builds selected-task detail for [item].
BacklogTaskDetailReadModel _detailFor(BacklogBoardItemReadModel item) {
  return BacklogTaskDetailReadModel(
    item: item,
    addedLabel: 'Added today',
    tags: item.tags,
    projectOptions: const [],
    suggestedSlots: const [],
  );
}
