import 'dart:io';

import 'package:scheduler_notifications/notifications.dart';
import 'package:test/test.dart';

import '../../scheduler_notifications/test/notification_adapter_contract.dart';

// ignore: avoid_relative_lib_imports
import '../lib/desktop_notifications.dart';

void main() {
  runNotificationAdapterContractTests('desktop', () {
    final backend = _RecordingBackend();
    return NotificationAdapterContractHarness(
      adapter: DesktopNotificationAdapter(backend: backend),
      scheduledRequests: () => backend.delivered,
      cancelledIds: () => backend.cancelled,
    );
  });

  test('desktop adapter delegates schedule and cancel to backend', () async {
    final backend = _RecordingBackend();
    final adapter = DesktopNotificationAdapter(backend: backend);
    final request = _request('reminder-1');

    await adapter.schedule(request);
    await adapter.cancel(' reminder-1 ');

    expect(backend.delivered, [request]);
    expect(backend.cancelled, ['reminder-1']);
    expect(adapter, isA<NotificationAdapter>());
  });

  test('linux backend invokes notify-send', () async {
    final calls = <_ProcessCall>[];
    final logs = <String>[];
    final backend = createDefaultDesktopNotificationBackend(
      platform: DesktopNotificationPlatform.linux,
      processRunner: (executable, arguments) async {
        calls.add(_ProcessCall(executable, arguments));
        return ProcessResult(1, 0, '', '');
      },
      logSink: logs.add,
    );

    await backend.deliver(_request('reminder-1'));

    expect(backend.isSupported, isTrue);
    expect(calls.single.executable, 'notify-send');
    expect(calls.single.arguments, contains('Start task'));
    expect(logs, isEmpty);
  });

  test('macOS backend invokes osascript', () async {
    final calls = <_ProcessCall>[];
    final backend = createDefaultDesktopNotificationBackend(
      platform: DesktopNotificationPlatform.macos,
      processRunner: (executable, arguments) async {
        calls.add(_ProcessCall(executable, arguments));
        return ProcessResult(1, 0, '', '');
      },
    );

    await backend.deliver(_request('reminder-1'));

    expect(backend.isSupported, isTrue);
    expect(calls.single.executable, 'osascript');
    expect(calls.single.arguments, contains('-e'));
  });

  test('unsupported and Windows platforms fall back to log output', () async {
    final logs = <String>[];
    final windows = createDefaultDesktopNotificationBackend(
      platform: DesktopNotificationPlatform.windows,
      logSink: logs.add,
    );
    final unsupported = createDefaultDesktopNotificationBackend(
      platform: DesktopNotificationPlatform.unsupported,
      logSink: logs.add,
    );

    await windows.deliver(_request('windows-reminder'));
    await unsupported.cancel(' unsupported-reminder ');

    expect(windows.isSupported, isFalse);
    expect(unsupported.isSupported, isFalse);
    expect(logs.first, contains('windows-reminder'));
    expect(logs.last, contains('unsupported-reminder'));
  });

  test('fake adapter can stand in for scheduling workflows', () async {
    final adapter = FakeNotificationAdapter();
    addTearDown(adapter.close);
    await _scheduleAndCancelReminder(adapter, _request('reminder-1'));

    expect(adapter.scheduledRequests, isEmpty);
    expect(adapter.cancelledIds, ['reminder-1']);
  });

  test('default desktop adapter can be created on supported test platforms',
      () {
    if (!Platform.isLinux && !Platform.isMacOS && !Platform.isWindows) {
      markTestSkipped(
          'Desktop notifications are unsupported on this platform.');
    }

    expect(DesktopNotificationAdapter.defaultInstance(),
        isA<NotificationAdapter>());
  });
}

Future<void> _scheduleAndCancelReminder(
  NotificationAdapter adapter,
  NotificationRequest request,
) async {
  await adapter.schedule(request);
  await adapter.cancel(request.id);
}

NotificationRequest _request(String id) {
  return NotificationRequest(
    id: id,
    title: 'Start task',
    body: 'Time to begin.',
    scheduledDateTimeUtc: DateTime.utc(2026, 1, 1, 9),
  );
}

final class _RecordingBackend implements DesktopNotificationBackend {
  final List<NotificationRequest> delivered = <NotificationRequest>[];
  final List<String> cancelled = <String>[];

  @override
  bool get isSupported => true;

  @override
  Future<void> deliver(NotificationRequest request) async {
    delivered.add(request);
  }

  @override
  Future<void> cancel(String id) async {
    cancelled.add(id);
  }
}

final class _ProcessCall {
  const _ProcessCall(this.executable, this.arguments);

  final String executable;
  final List<String> arguments;
}
