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

typedef OperationContextFactory =
    ApplicationOperationContext Function(String operationId);

typedef ReadRefresh = Future<void> Function();

sealed class SchedulerCommandState {
  const SchedulerCommandState();
}

class SchedulerCommandIdle extends SchedulerCommandState {
  const SchedulerCommandIdle();
}

class SchedulerCommandRunning extends SchedulerCommandState {
  const SchedulerCommandRunning(this.label);

  final String label;
}

class SchedulerCommandSuccess extends SchedulerCommandState {
  const SchedulerCommandSuccess(this.message);

  final String message;
}

class SchedulerCommandFailure extends SchedulerCommandState {
  const SchedulerCommandFailure({this.failure, this.error});

  final ApplicationFailure? failure;
  final Object? error;

  String get codeLabel {
    final typed = failure;
    if (typed != null) {
      return typed.detailCode ?? typed.code.name;
    }
    return 'unexpected';
  }
}

class SchedulerCommandController extends ChangeNotifier {
  SchedulerCommandController({
    required this.commands,
    required this.contextFor,
    required this.localDate,
    required this.refreshReads,
  });

  final V1ApplicationCommandUseCases commands;
  final OperationContextFactory contextFor;
  final CivilDate localDate;
  final ReadRefresh refreshReads;
  var _sequence = 0;
  SchedulerCommandState _state = const SchedulerCommandIdle();

  SchedulerCommandState get state => _state;

  Future<void> quickCaptureToBacklog(String title) async {
    await _run(
      label: 'Capturing task',
      successMessage: 'Task captured',
      refreshAfterSuccess: true,
      action: (operationId) {
        return commands.quickCaptureToBacklog(
          context: contextFor(operationId),
          title: title,
          projectId: 'home',
        );
      },
    );
  }

  Future<void> scheduleBacklogItem({
    required String taskId,
    required int durationMinutes,
    required DateTime expectedUpdatedAt,
  }) async {
    await _run(
      label: 'Scheduling task',
      successMessage: 'Task scheduled',
      refreshAfterSuccess: true,
      action: (operationId) {
        return commands.scheduleBacklogItemToNextAvailableSlot(
          context: contextFor(operationId),
          localDate: localDate,
          taskId: taskId,
          durationMinutes: durationMinutes,
          expectedUpdatedAt: expectedUpdatedAt,
        );
      },
    );
  }

  Future<void> completeFlexibleTask({
    required String taskId,
    DateTime? expectedUpdatedAt,
  }) async {
    await _run(
      label: 'Completing task',
      successMessage: 'Task completed',
      refreshAfterSuccess: true,
      action: (operationId) {
        return commands.completeFlexibleTask(
          context: contextFor(operationId),
          localDate: localDate,
          taskId: taskId,
          expectedUpdatedAt: expectedUpdatedAt,
        );
      },
    );
  }

  Future<void> _run({
    required String label,
    required String successMessage,
    required bool refreshAfterSuccess,
    required Future<ApplicationResult<ApplicationCommandResult>> Function(
      String operationId,
    )
    action,
  }) async {
    final operationId = _nextOperationId();
    _setState(SchedulerCommandRunning(label));
    try {
      final result = await action(operationId);
      final failure = result.failure;
      if (failure != null) {
        _setState(SchedulerCommandFailure(failure: failure));
        return;
      }
      if (refreshAfterSuccess) {
        await refreshReads();
      }
      _setState(SchedulerCommandSuccess(successMessage));
    } on Object catch (error) {
      _setState(SchedulerCommandFailure(error: error));
    }
  }

  String _nextOperationId() {
    _sequence += 1;
    return 'ui-command-$_sequence';
  }

  void _setState(SchedulerCommandState next) {
    _state = next;
    notifyListeners();
  }
}
