import 'dart:io';

import 'package:flutter_local_notifications/flutter_local_notifications.dart';

import '../about/external/data/Commitment.dart';
import '../about/external/data/LiveSession.dart';
import '../about/internal/application/CommitmentClass.dart';
import '../about/internal/application/EscalationTier.dart';
import '../about/internal/application/ToneLevel.dart';
import '../utils/CommonUtils.dart';
import '../utils/ToneEngine.dart';

/// Notification channels, the ongoing "task running" notification, and the
/// full-screen alarm used for non-negotiables.
class LocalNotificationEngine {
  static final FlutterLocalNotificationsPlugin plugin =
      FlutterLocalNotificationsPlugin();

  /// Ordinary reminders — respects quiet hours.
  static const String reminderChannel = "grounded_reminders";

  /// The ongoing notification attached to a running task. Not dismissable, so
  /// a task in progress is always one tap away from the lock screen.
  static const String sessionChannel = "grounded_session";

  /// Alarm-class, full-screen. Reserved for non-negotiables, and the only
  /// channel that overrides quiet hours.
  static const String alarmChannel = "grounded_alarm";

  static const int sessionNotificationId = 9000;

  static bool _ready = false;

  static Future<void> init() async {
    if (_ready) {
      return;
    }

    const AndroidInitializationSettings android =
        AndroidInitializationSettings('@mipmap/ic_launcher');

    const DarwinInitializationSettings apple = DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
      // Critical alerts need Apple entitlement approval; requesting without it
      // is simply ignored rather than failing.
      requestCriticalPermission: true,
    );

    await plugin.initialize(
      settings: const InitializationSettings(
          android: android, iOS: apple, macOS: apple),
    );

    if (Platform.isAndroid) {
      await _createAndroidChannels();
    }

    _ready = true;
  }

  static Future<void> _createAndroidChannels() async {
    final AndroidFlutterLocalNotificationsPlugin? android =
        plugin.resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>();

    if (android == null) {
      return;
    }

    await android.createNotificationChannel(const AndroidNotificationChannel(
      reminderChannel,
      'Reminders',
      description: 'Nudges about commitments that are due.',
      importance: Importance.defaultImportance,
    ));

    await android.createNotificationChannel(const AndroidNotificationChannel(
      sessionChannel,
      'Task in progress',
      description: 'The ongoing notification for a task you are running.',
      importance: Importance.low,
      playSound: false,
      enableVibration: false,
    ));

    await android.createNotificationChannel(const AndroidNotificationChannel(
      alarmChannel,
      'Non-negotiables',
      description:
          'Full-screen alarms for non-negotiable commitments. These ignore quiet hours.',
      importance: Importance.max,
      playSound: true,
      enableVibration: true,
    ));

    await android.requestNotificationsPermission();

    // Exact alarms are what let a window actually close on time rather than
    // whenever the OS feels like it.
    await android.requestExactAlarmsPermission();
  }

  /// The ongoing notification for a running task. Shows the live remaining
  /// time so it is useful from the lock screen without unlocking.
  static Future<void> showSessionNotification(LiveSession session) async {
    await init();

    final String remaining = session.requiredSeconds > 0
        ? "${formatClock(session.remainingSeconds())} remaining"
        : "${formatClock(session.elapsedSeconds())} elapsed";

    final AndroidNotificationDetails android = AndroidNotificationDetails(
      sessionChannel,
      'Task in progress',
      channelDescription: 'The ongoing notification for a running task.',
      importance: Importance.low,
      priority: Priority.low,
      ongoing: true,
      autoCancel: false,
      onlyAlertOnce: true,
      showWhen: true,
      usesChronometer: session.requiredSeconds <= 0,
      category: AndroidNotificationCategory.workout,
      actions: const <AndroidNotificationAction>[
        AndroidNotificationAction('pause', 'Pause'),
        AndroidNotificationAction('finish', 'Finish'),
      ],
    );

    await plugin.show(
      id: sessionNotificationId,
      title: session.title,
      body: session.goalTitle.isEmpty
          ? remaining
          : "${session.goalTitle} · $remaining",
      notificationDetails: NotificationDetails(
        android: android,
        iOS: const DarwinNotificationDetails(presentBanner: false),
      ),
      payload: session.commitmentId,
    );
  }

  static Future<void> cancelSessionNotification() async {
    await init();
    await plugin.cancel(id: sessionNotificationId);
  }

  /// A due-window nudge. Non-negotiables go out full-screen and alarm-class so
  /// they surface over the lock screen; everything else is an ordinary
  /// notification.
  static Future<void> showCommitmentNudge(
    Commitment commitment,
    EscalationTier tier,
    ToneLevel tone,
  ) async {
    await init();

    final bool nonNegotiable =
        commitment.commitmentClass == CommitmentClass.NonNegotiable;

    final AndroidNotificationDetails android = AndroidNotificationDetails(
      nonNegotiable ? alarmChannel : reminderChannel,
      nonNegotiable ? 'Non-negotiables' : 'Reminders',
      importance: nonNegotiable ? Importance.max : Importance.defaultImportance,
      priority: nonNegotiable ? Priority.max : Priority.defaultPriority,
      // The full-screen intent is what turns this into a lock-screen takeover
      // rather than a banner that can be swiped past.
      fullScreenIntent: nonNegotiable,
      category: nonNegotiable
          ? AndroidNotificationCategory.alarm
          : AndroidNotificationCategory.reminder,
      actions: <AndroidNotificationAction>[
        const AndroidNotificationAction('start', 'Start now'),
        if (!nonNegotiable)
          const AndroidNotificationAction('snooze', 'Snooze'),
      ],
    );

    await plugin.show(
      id: commitment.id.hashCode,
      title: commitment.title,
      body: ToneEngine.nudge(tier, tone, commitment.title),
      notificationDetails: NotificationDetails(
        android: android,
        iOS: DarwinNotificationDetails(
          // Critical alerts bypass Do Not Disturb, and are the iOS equivalent
          // of the Android full-screen intent. Requires Apple approval.
          interruptionLevel: nonNegotiable
              ? InterruptionLevel.critical
              : InterruptionLevel.active,
        ),
      ),
      payload: commitment.id,
    );
  }

  static Future<void> cancelAll() async {
    await init();
    await plugin.cancelAll();
  }
}
