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

/// Composes the FocusFlow Flutter application around Demo Scheduler Composition.
library;

import 'package:scheduler_core/scheduler_core.dart';

import '../controllers/backlog_board_controller.dart';
import '../controllers/scheduler_command_controller.dart';
import '../controllers/scheduler_read_controller.dart';
import '../controllers/selected_date_controller.dart';
import '../controllers/today_screen_controller.dart';
import 'scheduler_app_composition.dart';

part 'demo/demo_date_formatter.dart';
part 'demo/demo_seed_tasks.dart';

/// Wires the Flutter demo UI to in-memory scheduler application use cases.
class DemoSchedulerComposition implements SchedulerAppComposition {
  /// Creates a `DemoSchedulerComposition._` 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.
  DemoSchedulerComposition._({
    required this.store,
    required this.todayQuery,
    required this.managementUseCases,
    required this.commandUseCases,
    required this.date,
    required this.readAt,
  });

  /// Fixed owner used by the seeded demo data.
  static const ownerId = 'owner-1';

  /// Fixed owner time zone used by the seeded demo data.
  static const timeZoneId = 'UTC';

  /// Fixed project id used by the seeded demo data.
  static const projectId = 'home';

  /// In-memory application store backing the demo.
  final InMemoryApplicationUnitOfWork store;

  /// Query object used to read the Today state.
  final GetTodayStateQuery todayQuery;

  /// Management use cases used by backlog-facing UI surfaces.
  final V1ApplicationManagementUseCases managementUseCases;

  /// Command use cases used by interactive UI controls.
  final V1ApplicationCommandUseCases commandUseCases;

  /// Local date represented by the seeded demo.
  final CivilDate date;

  /// Stable read clock used for deterministic demo operations.
  final DateTime readAt;

  /// Human-readable label for [date].
  @override
  String get dateLabel => dateLabelFor(date);

  /// Initial selected owner-local planning date.
  @override
  CivilDate get initialDate => date;

  /// Default project id used by quick capture.
  @override
  String get defaultProjectId => projectId;

  /// Formats [date] for compact UI date labels.
  @override
  String dateLabelFor(CivilDate date) {
    return formatSchedulerDateLabel(date);
  }

  /// Creates the controller that owns the visible planning date.
  @override
  SelectedDateController createSelectedDateController() {
    return SelectedDateController(initialDate: initialDate);
  }

  /// Creates a deterministic seeded composition for the demo app and tests.
  factory DemoSchedulerComposition.seeded({
    Clock? clock,
    CivilDate? selectedDate,
    bool includeTodayItems = true,
    bool includeBacklogItems = true,
  }) {
    final resolvedDate =
        selectedDate ??
        CivilDate.fromDateTime((clock ?? const SystemClock()).now());
    final readAt = _instant(resolvedDate, 15, 55);
    final createdAt = _instant(resolvedDate, 8);
    final projects = _demoProjects();
    final tasks = <Task>[
      if (includeTodayItems) ..._todaySeedTasks(resolvedDate, createdAt),
      if (includeBacklogItems) ..._backlogSeedTasks(resolvedDate, createdAt),
    ];
    final store = InMemoryApplicationUnitOfWork(
      initialProjects: projects,
      initialOwnerSettings: [
        OwnerSettings(
          ownerId: ownerId,
          timeZoneId: timeZoneId,
          compactModeEnabled: true,
        ),
      ],
      initialTasks: tasks,
    );

    return DemoSchedulerComposition._(
      store: store,
      todayQuery: GetTodayStateQuery(
        applicationStore: store,
        timeZoneResolver: const FixedOffsetTimeZoneResolver.utc(),
      ),
      managementUseCases: V1ApplicationManagementUseCases(
        applicationStore: store,
      ),
      commandUseCases: V1ApplicationCommandUseCases(
        applicationStore: store,
        timeZoneResolver: const FixedOffsetTimeZoneResolver.utc(),
      ),
      date: resolvedDate,
      readAt: readAt,
    );
  }

  /// Creates the controller used by the compact Today screen mockup.
  @override
  TodayScreenController createTodayScreenController({
    required SelectedDateController selectedDates,
  }) {
    return TodayScreenController(
      selectedDates: selectedDates,
      dateLabelFor: dateLabelFor,
      now: () => readAt,
      read: (date) {
        return todayQuery.execute(
          GetTodayStateRequest(
            context: context('ui-plan-1-read-today'),
            date: date,
            includeNextFlexibleItem: true,
          ),
        );
      },
    );
  }

  /// Creates the controller used by the Backlog board screen.
  @override
  BacklogBoardController createBacklogBoardController({
    required SelectedDateController selectedDates,
  }) {
    return BacklogBoardController(
      selectedDates: selectedDates,
      dateLabelFor: dateLabelFor,
      readBoard: (request) {
        return managementUseCases.getBacklogBoard(
          GetBacklogBoardRequest(
            context: context('ui-read-backlog-board'),
            localDate: request.localDate,
            searchText: request.searchText,
            filters: request.filters,
            sortKey: request.sortKey,
            groupMode: request.groupMode,
          ),
        );
      },
      readTaskDetail: (taskId, localDate) {
        return managementUseCases.getBacklogTaskDetail(
          GetBacklogTaskDetailRequest(
            context: context('ui-read-backlog-detail'),
            localDate: localDate,
            taskId: taskId,
          ),
        );
      },
    );
  }

  /// Creates a generic read controller for Today-state panes.
  UiReadController<TodayState> createTodayController() {
    return ApplicationReadController<TodayState>(
      read: () {
        return todayQuery.execute(
          GetTodayStateRequest(
            context: context('ui-read-today'),
            date: date,
            includeNextFlexibleItem: true,
          ),
        );
      },
      isEmpty: (state) {
        return state.timelineItems.isEmpty && state.pendingNotices.isEmpty;
      },
    );
  }

  /// Creates a generic read controller for backlog panes.
  UiReadController<BacklogQueryResult> createBacklogController() {
    return ApplicationReadController<BacklogQueryResult>(
      read: () {
        return managementUseCases.getBacklog(
          GetBacklogRequest(
            context: context('ui-read-backlog'),
            sortKey: BacklogSortKey.age,
          ),
        );
      },
      isEmpty: (state) => state.items.isEmpty,
    );
  }

  /// Creates the command controller used by interactive scheduler controls.
  @override
  SchedulerCommandController createCommandController({
    required ReadRefresh refreshReads,
    required SelectedDateReader selectedDate,
  }) {
    return SchedulerCommandController(
      commands: commandUseCases,
      contextFor: context,
      selectedDate: selectedDate,
      refreshReads: refreshReads,
      quickCaptureProjectId: defaultProjectId,
      operationIdPrefix: 'ui-command-${readAt.microsecondsSinceEpoch}',
    );
  }

  /// Creates an application operation context for the supplied [operationId].
  ApplicationOperationContext context(String operationId, {DateTime? now}) {
    return ApplicationOperationContext.start(
      operationId: operationId,
      ownerTimeZone: OwnerTimeZoneContext(
        ownerId: ownerId,
        timeZoneId: timeZoneId,
      ),
      clock: FixedClock(now ?? readAt),
      idGenerator: SequentialIdGenerator(prefix: operationId),
    );
  }

  /// Releases demo composition resources.
  @override
  Future<void> dispose() async {}
}
