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

/// Exercises Scheduler Flow integration behavior for the Scheduler Integration Tests package.
library;

import 'package:drift/native.dart';
import 'package:scheduler_core/scheduler_core.dart' as core;
import 'package:scheduler_notifications/notifications.dart';
import 'package:scheduler_persistence_memory/persistence_memory.dart';
import 'package:scheduler_persistence_sqlite/sqlite.dart';
import 'package:test/test.dart';

/// Entry point for this executable Dart file.
/// It wires the local command, test, or application behavior needed by this repository without exposing additional public API.
void main() {
  test('scheduler flow schedules, notifies, persists, and reloads', () async {
    final runtime = Stopwatch()..start();
    addTearDown(() {
      runtime.stop();
      expect(runtime.elapsed, lessThan(const Duration(seconds: 2)));
    });
    final ownerId = core.OwnerId('owner-a');
    final now = DateTime.utc(2026, 6, 26, 8);
    final window = core.SchedulingWindow(
      start: DateTime.utc(2026, 6, 26, 9),
      end: DateTime.utc(2026, 6, 26, 10, 30),
    );
    final lockedInterval = core.TimeInterval(
      start: DateTime.utc(2026, 6, 26, 9),
      end: DateTime.utc(2026, 6, 26, 9, 15),
      label: 'Protected setup time',
    );
    final backlogTask = core.Task(
      id: 'backlog-a',
      title: 'Write planning note',
      projectId: 'project-a',
      type: core.TaskType.flexible,
      status: core.TaskStatus.backlog,
      durationMinutes: 15,
      createdAt: now,
      updatedAt: now,
    );
    final plannedTask = core.Task(
      id: 'planned-a',
      title: 'Existing focus block',
      projectId: 'project-a',
      type: core.TaskType.flexible,
      status: core.TaskStatus.planned,
      durationMinutes: 30,
      scheduledStart: DateTime.utc(2026, 6, 26, 9, 30),
      scheduledEnd: DateTime.utc(2026, 6, 26, 10),
      createdAt: now,
      updatedAt: now,
    );

    final memoryTasks = InMemoryTaskRepository();
    expect(
      (await memoryTasks.insert(ownerId: ownerId, task: backlogTask)).isRight,
      isTrue,
    );
    expect(
      (await memoryTasks.insert(ownerId: ownerId, task: plannedTask)).isRight,
      isTrue,
    );

    final loaded = await memoryTasks.findByOwner(ownerId: ownerId);
    expect(loaded.isRight, isTrue);
    final result =
        const core.SchedulingEngine().insertBacklogTaskIntoNextAvailableSlot(
      input: core.SchedulingInput(
        tasks: loaded.right.items,
        window: window,
        lockedIntervals: [lockedInterval],
      ),
      taskId: backlogTask.id,
      updatedAt: now,
    );

    expect(result.outcomeCode, core.SchedulingOutcomeCode.success);
    final scheduledBacklog = _taskById(result.tasks, backlogTask.id);
    expect(scheduledBacklog.status, core.TaskStatus.planned);
    expect(scheduledBacklog.scheduledStart, lockedInterval.end);
    expect(
      scheduledBacklog.scheduledEnd,
      DateTime.utc(2026, 6, 26, 9, 30),
    );

    final notifications = FakeNotificationAdapter();
    addTearDown(notifications.close);
    await notifications.schedule(NotificationRequest(
      id: 'task-${scheduledBacklog.id}-start',
      title: scheduledBacklog.title,
      body: 'Scheduled task is ready.',
      scheduledDateTimeUtc: scheduledBacklog.scheduledStart!,
      payloadJson: '{"taskId":"${scheduledBacklog.id}"}',
    ));
    expect(
      notifications.scheduledRequests.keys,
      contains('task-backlog-a-start'),
    );

    final db = SchedulerDb(NativeDatabase.memory());
    addTearDown(db.close);
    final sqliteTasks = SqliteTaskRepository(db);
    for (final task in result.tasks) {
      final inserted = await sqliteTasks.insert(ownerId: ownerId, task: task);
      expect(inserted.isRight, isTrue);
    }

    final reloaded = await sqliteTasks.findById(
      ownerId: ownerId,
      taskId: backlogTask.id,
    );
    expect(reloaded.isRight, isTrue);
    final persistedBacklog = reloaded.right;
    expect(persistedBacklog, isNotNull);
    expect(persistedBacklog!.scheduledStart, scheduledBacklog.scheduledStart);
    expect(persistedBacklog.scheduledEnd, scheduledBacklog.scheduledEnd);
    expect(persistedBacklog.status, core.TaskStatus.planned);
  });
}

/// Top-level helper that performs the `_taskById` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.Task _taskById(Iterable<core.Task> tasks, String id) {
  return tasks.singleWhere((task) => task.id == id);
}
