import 'dart:async';

import 'package:scheduler_notifications/notifications.dart';

/// Supported desktop platform categories.
enum DesktopNotificationPlatform {
  /// Linux desktop.
  linux,

  /// macOS desktop.
  macos,

  /// Windows desktop.
  windows,

  /// Unsupported or unknown platform.
  unsupported,
}

/// Logging callback used by fallback notification backends.
typedef DesktopNotificationLogSink = void Function(String line);

/// Platform-neutral backend used by [DesktopNotificationAdapter].
abstract interface class DesktopNotificationBackend {
  /// Whether this backend has native notification support.
  bool get isSupported;

  /// Deliver [request].
  Future<void> deliver(NotificationRequest request);

  /// Cancel a pending notification by [id], when supported.
  Future<void> cancel(String id);
}

/// Notification adapter that delegates to a desktop backend.
final class DesktopNotificationAdapter implements NotificationAdapter {
  /// Creates an adapter backed by [backend].
  DesktopNotificationAdapter({required DesktopNotificationBackend backend})
      : _backend = backend;

  final DesktopNotificationBackend _backend;

  /// Creates the best available desktop adapter for the current platform.
  factory DesktopNotificationAdapter.defaultInstance({
    DesktopNotificationPlatform? platform,
    Object? processRunner,
    DesktopNotificationLogSink? logSink,
  }) {
    return DesktopNotificationAdapter(
      backend: createDefaultDesktopNotificationBackend(
        platform: platform,
        processRunner: processRunner,
        logSink: logSink,
      ),
    );
  }

  @override
  Stream<NotificationFeedback> get feedback => const Stream.empty();

  @override
  Future<void> schedule(NotificationRequest request) {
    return _backend.deliver(request);
  }

  @override
  Future<void> cancel(String id) {
    return _backend.cancel(id.trim());
  }
}

/// Create the best default desktop notification backend.
DesktopNotificationBackend createDefaultDesktopNotificationBackend({
  DesktopNotificationPlatform? platform,
  Object? processRunner,
  DesktopNotificationLogSink? logSink,
}) {
  return StdoutNotificationBackend(
    logSink: logSink ?? _defaultLogSink,
    platform: platform ?? DesktopNotificationPlatform.unsupported,
  );
}

/// Fallback backend that writes notification requests to stdout/logs.
final class StdoutNotificationBackend implements DesktopNotificationBackend {
  /// Creates a stdout fallback backend.
  StdoutNotificationBackend({
    required DesktopNotificationLogSink logSink,
    required this.platform,
  }) : _logSink = logSink;

  final DesktopNotificationLogSink _logSink;

  /// Platform this fallback represents.
  final DesktopNotificationPlatform platform;

  @override
  bool get isSupported => false;

  @override
  Future<void> deliver(NotificationRequest request) async {
    _logSink(
      'notification[$platform] ${request.id}: ${request.title} - '
      '${request.body}',
    );
  }

  @override
  Future<void> cancel(String id) async {
    _logSink('notification[$platform] cancel: ${id.trim()}');
  }
}

void _defaultLogSink(String line) {
  // ignore: avoid_print
  print(line);
}
