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

part of '../../../desktop_notification_adapter_io.dart';

/// Linux backend using `notify-send`.
final class LinuxNotifySendBackend implements DesktopNotificationBackend {
  /// Creates a Linux command-backed backend.
  LinuxNotifySendBackend({
    required DesktopProcessRunner processRunner,
    required DesktopNotificationBackend fallback,
  })  : _processRunner = processRunner,
        _fallback = fallback;

  /// Private state stored as `_processRunner` for this implementation.
  /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
  final DesktopProcessRunner _processRunner;

  /// Private state stored as `_fallback` for this implementation.
  /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
  final DesktopNotificationBackend _fallback;

  /// Returns the derived `isSupported` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  @override
  bool get isSupported => true;

  /// Runs the `deliver` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<void> deliver(NotificationRequest request) async {
    logger.debug(() => 'Linux notification delivery requested. '
        'notificationId=${request.id} backend=notify-send');
    await _runOrFallback(
      request,
      () => _processRunner('notify-send', <String>[
        '--app-name=ADHD Scheduler',
        request.title,
        request.body,
      ]),
    );
  }

  /// Runs the `cancel` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<void> cancel(String id) async {
    logger.finer(() => 'Linux notification cancel delegated to fallback. '
        'notificationId=${id.trim()}');
    await _fallback.cancel(id);
  }

  /// Runs the `_runOrFallback` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  Future<void> _runOrFallback(
    NotificationRequest request,
    Future<ProcessResult> Function() run,
  ) async {
    try {
      final result = await run();
      if (result.exitCode != 0) {
        logger.warn(() => 'Linux notification command failed; using fallback. '
            'notificationId=${request.id} exitCode=${result.exitCode}');
        await _fallback.deliver(request);
      } else {
        logger.debug(() => 'Linux notification command completed. '
            'notificationId=${request.id} exitCode=${result.exitCode}');
      }
    } on ProcessException catch (error, stackTrace) {
      logger.warn(
        () => 'Linux notification command unavailable; using fallback. '
            'notificationId=${request.id}',
        error: error,
        stackTrace: stackTrace,
      );
      await _fallback.deliver(request);
    }
  }
}
