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

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

/// macOS backend using `osascript`.
final class MacOsNotificationBackend implements DesktopNotificationBackend {
  /// Creates a macOS command-backed backend.
  MacOsNotificationBackend({
    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(() => 'macOS notification delivery requested. '
        'notificationId=${request.id} backend=osascript');
    final script = 'display notification ${_appleScriptString(request.body)} '
        'with title ${_appleScriptString(request.title)}';
    try {
      final result = await _processRunner('osascript', <String>['-e', script]);
      if (result.exitCode != 0) {
        logger.warn(() => 'macOS notification command failed; using fallback. '
            'notificationId=${request.id} exitCode=${result.exitCode}');
        await _fallback.deliver(request);
      } else {
        logger.debug(() => 'macOS notification command completed. '
            'notificationId=${request.id} exitCode=${result.exitCode}');
      }
    } on ProcessException catch (error, stackTrace) {
      logger.warn(
        () => 'macOS notification command unavailable; using fallback. '
            'notificationId=${request.id}',
        error: error,
        stackTrace: stackTrace,
      );
      await _fallback.deliver(request);
    }
  }

  /// 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(() => 'macOS notification cancel delegated to fallback. '
        'notificationId=${id.trim()}');
    await _fallback.cancel(id);
  }
}
