import 'dart:convert';
import 'dart:async';
import 'dart:typed_data';

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

/// Background notification handler - MUST be at top level
/// Called when notification received while app is backgrounded/killed
@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
  debugPrint(
    '🔔 [BACKGROUND] Notification tapped: payload length=${notificationResponse.payload?.length ?? 0}',
  );

  // Parse dan handle watering execution
  if (notificationResponse.payload != null) {
    try {
      final data =
          jsonDecode(notificationResponse.payload!) as Map<String, dynamic>;
      final type = data['type'] as String?;

      if (type == 'watering_execution') {
        debugPrint(
          '💧 [BACKGROUND] Watering execution triggered from notification tap',
        );
        // Call static method untuk handle watering
        NotificationService.handleWateringNotificationTap(data);
      }
    } catch (e) {
      debugPrint('⚠️ [BACKGROUND] Error parsing notification: $e');
    }
  }
}

/// 🔔 NOTIFICATION SERVICE v2.0 - WITH BACKGROUND SUPPORT & WATERING SCHEDULING
///
/// Fitur Utama:
/// ✅ Exact alarm scheduling untuk precision timing
/// ✅ Background notifications (muncul bahkan saat app minimized!)
/// ✅ Retry logic untuk reliability
/// ✅ Multiple notification channels (watering, alert, info)
/// ✅ Full Firebase Cloud Messaging support
/// ✅ Proper permission handling
/// ✅ NEW: Watering schedule execution tanpa worker!
///
/// 💧 PENJADWALAN PENYIRAMAN (Watering Scheduling):
/// - Uses exact alarm (AndroidScheduleMode.exactAllowWhileIdle)
/// - Schedule watering execution saat alarm fire (bukan hanya notifikasi)
/// - Works even when app is minimized/killed
/// - Eliminates dependency to background worker (railway-worker, etc)
///
/// Cara Penggunaan:
/// 1. Saat app starts: NotificationService.initialize()
/// 2. Setup callback: NotificationService.setOnWateringScheduleCallback(callback)
/// 3. Schedule watering: NotificationService.scheduleAllWateringExecutions()
/// 4. Callback akan dipanggil saat alarm fire untuk eksekusi penyiraman
///
/// Integrasi dengan KontrolAutomationService:
/// - startWaktuMode() akan otomatis schedule semua jadwal via notification service
/// - Jadwal changes akan otomatis re-sync ke scheduler via JadwalService
/// - stopWaktuMode() akan cancel semua scheduled watering executions
class NotificationService {
  NotificationService._();

  static final NotificationService instance = NotificationService._();

  static const String topicName = 'irrigo_notifications';

  // Callback untuk handle watering execution dari scheduled alarm
  // ⚠️ IMPORTANT: Must be public static for background notification handler
  static Function(dynamic)? _onWateringScheduleCallback;
  static const String wateringChannelId = 'irrigo_watering_channel';
  static const String alertChannelId = 'irrigo_alert_channel';
  static const String infoChannelId = 'irrigo_info_channel';
  static const int _maxAndroidNotificationId = 2147483647;

  final FlutterLocalNotificationsPlugin _plugin =
      FlutterLocalNotificationsPlugin();

  bool _isInitialized = false;
  bool _hasNotificationPermission =
      false; // CRITICAL: Track if user granted permission!
  int _notificationCounter = 0;

  /// ==================== INITIALIZATION ====================

  /// Initialize notification service dengan complete setup
  Future<void> initialize() async {
    if (_isInitialized || kIsWeb) return;

    debugPrint('🔔 Initializing Notification Service v2.0...');

    // Setup timezones
    tz.initializeTimeZones();
    try {
      tz.setLocalLocation(tz.getLocation('Asia/Jakarta'));
    } catch (_) {
      tz.setLocalLocation(tz.UTC);
    }

    // Android & iOS initialization
    const androidSettings = AndroidInitializationSettings(
      '@mipmap/ic_launcher',
    );
    const iosSettings = DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
      defaultPresentAlert: true,
      defaultPresentBadge: true,
      defaultPresentSound: true,
    );

    const initSettings = InitializationSettings(
      android: androidSettings,
      iOS: iosSettings,
    );

    // Initialize dengan background response support
    await _plugin.initialize(
      initSettings,
      onDidReceiveNotificationResponse: _onNotificationResponse,
      onDidReceiveBackgroundNotificationResponse:
          notificationTapBackground, // ← Background handler untuk minimize/kill
    );

    // Setup channels & permissions
    await _setupNotificationChannels();
    _hasNotificationPermission = await _requestAllPermissions();
    debugPrint(
      _hasNotificationPermission
          ? '✅ POST_NOTIFICATIONS permission granted'
          : '⚠️ POST_NOTIFICATIONS permission DENIED - notifications will not show!',
    );

    // Firebase setup
    try {
      await FirebaseMessaging.instance.subscribeToTopic(topicName);
      debugPrint('✅ Subscribed to Firebase topic: $topicName');
    } catch (e) {
      debugPrint('⚠️ Firebase subscription failed: $e');
    }

    _setupFCMHandlers();
    _isInitialized = true;
    debugPrint('✅ Notification Service v2.0 ready!');
  }

  /// Set callback untuk handle watering execution saat scheduled alarm fire
  /// Callback akan menerima schedule data (scheduleId, durasi, pots, dll)
  static void setOnWateringScheduleCallback(Function(dynamic)? callback) {
    _onWateringScheduleCallback = callback;
    debugPrint(
      callback != null
          ? '✅ Watering schedule callback registered'
          : '⚠️ Watering schedule callback cleared',
    );
  }

  /// ⭐ Static method untuk handle watering notification tap (from background)
  /// Called dari background notification handler
  static Future<void> handleWateringNotificationTap(
    Map<String, dynamic> data,
  ) async {
    try {
      debugPrint(
        '💧 [STATIC] Handling watering from background notification tap',
      );

      // Invoke callback jika registered
      if (_onWateringScheduleCallback != null) {
        debugPrint('💧 [STATIC] Invoking callback dengan data: ${data.keys}');
        await Future.sync(() => _onWateringScheduleCallback?.call(data));
      } else {
        debugPrint('⚠️ [STATIC] No callback registered for watering execution');
      }
    } catch (e) {
      debugPrint('❌ [STATIC] Error handling watering notification: $e');
    }
  }

  /// Re-validate permissions (not cached) - call before critical operations
  Future<bool> revalidatePermissions() async {
    if (kIsWeb) return false;

    try {
      final androidPlugin =
          _plugin
              .resolvePlatformSpecificImplementation<
                AndroidFlutterLocalNotificationsPlugin
              >();

      // Fresh check of POST_NOTIFICATIONS
      bool postNotifGranted = false;
      try {
        postNotifGranted =
            await androidPlugin?.requestNotificationsPermission() ?? false;
      } catch (_) {
        postNotifGranted = false;
      }

      _hasNotificationPermission = postNotifGranted;
      return postNotifGranted;
    } catch (e) {
      debugPrint('❌ Permission revalidation error: $e');
      return false;
    }
  }

  /// Setup notification channels
  Future<void> _setupNotificationChannels() async {
    final androidPlugin =
        _plugin
            .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin
            >();

    if (androidPlugin == null) return;

    try {
      // Watering channel - PRIORITY TINGGI
      const wateringChannel = AndroidNotificationChannel(
        wateringChannelId,
        'IrriGo Penyiraman',
        description: 'Notifikasi penyiraman - selalu muncul!',
        importance: Importance.max,
        playSound: true,
        enableLights: true,
        enableVibration: true,
      );

      // Alert channel
      const alertChannel = AndroidNotificationChannel(
        alertChannelId,
        'IrriGo Alert',
        description: 'Alert dan warning dari sistem',
        importance: Importance.high,
        playSound: true,
        enableLights: true,
        enableVibration: true,
      );

      // Info channel
      const infoChannel = AndroidNotificationChannel(
        infoChannelId,
        'IrriGo Informasi',
        description: 'Informasi sistem IrriGo',
        importance: Importance.low,
        playSound: false,
        enableLights: false,
        enableVibration: false,
      );

      await Future.wait([
        androidPlugin.createNotificationChannel(wateringChannel),
        androidPlugin.createNotificationChannel(alertChannel),
        androidPlugin.createNotificationChannel(infoChannel),
      ]);
      debugPrint('✅ Notification channels created');
    } catch (e) {
      debugPrint('❌ Error creating channels: $e');
    }
  }

  /// Request all permissions - returns true if POST_NOTIFICATIONS permission likely granted
  Future<bool> _requestAllPermissions() async {
    try {
      final androidPlugin =
          _plugin
              .resolvePlatformSpecificImplementation<
                AndroidFlutterLocalNotificationsPlugin
              >();

      // Notifications permission
      await androidPlugin?.requestNotificationsPermission();

      // Exact alarms permission
      final canScheduleExact =
          await androidPlugin?.canScheduleExactNotifications() ?? false;
      if (!canScheduleExact) {
        await androidPlugin?.requestExactAlarmsPermission();
      }

      // iOS permissions
      await _plugin
          .resolvePlatformSpecificImplementation<
            IOSFlutterLocalNotificationsPlugin
          >()
          ?.requestPermissions(alert: true, badge: true, sound: true);

      // Firebase permissions
      await FirebaseMessaging.instance.requestPermission(
        alert: true,
        badge: true,
        sound: true,
        provisional: false,
      );

      debugPrint('✅ All permissions requested');
      return true; // ← Assume granted if no error thrown
    } catch (e) {
      debugPrint('⚠️ Permission error: $e');
      return false; // ← Permission request failed
    }
  }

  /// Setup FCM handlers
  void _setupFCMHandlers() {
    FirebaseMessaging.onMessage.listen((message) {
      debugPrint('📨 FCM onMessage: ${message.messageId}');
      _showRemoteMessage(message);
    });

    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      debugPrint('📨 FCM onMessageOpenedApp: ${message.messageId}');
    });
  }

  /// ==================== NOTIFICATION DISPLAY ====================

  /// Build watering notification details (HIGH PRIORITY) - FOR IMMEDIATE DISPLAY
  NotificationDetails _buildWateringNotificationDetails() {
    return NotificationDetails(
      android: AndroidNotificationDetails(
        wateringChannelId,
        'IrriGo Penyiraman',
        channelDescription: 'Notifikasi penyiraman',
        importance: Importance.max,
        priority: Priority.high,
        playSound: true,
        enableVibration: true,
        vibrationPattern: Int64List.fromList([0, 500, 200, 500]),
        enableLights: true,
        icon: '@mipmap/ic_launcher',
        largeIcon: const DrawableResourceAndroidBitmap('@mipmap/ic_launcher'),
        autoCancel: true,
        showWhen: true,
        fullScreenIntent: true,
        tag: 'watering',
      ),
      iOS: const DarwinNotificationDetails(
        presentAlert: true,
        presentBadge: true,
        presentSound: true,
        categoryIdentifier: 'watering',
      ),
    );
  }

  /// Build watering notification details FOR SCHEDULED (handles background firing)
  NotificationDetails _buildScheduledWateringNotificationDetails() {
    return NotificationDetails(
      android: AndroidNotificationDetails(
        wateringChannelId,
        'IrriGo Penyiraman',
        channelDescription: 'Notifikasi penyiraman',
        importance: Importance.max,
        priority: Priority.high,
        playSound: true,
        enableVibration: true,
        vibrationPattern: Int64List.fromList([0, 500, 200, 500]),
        enableLights: true,
        icon: '@mipmap/ic_launcher',
        largeIcon: const DrawableResourceAndroidBitmap('@mipmap/ic_launcher'),
        autoCancel: true,
        showWhen: true,
        tag: 'watering',
      ),
      iOS: const DarwinNotificationDetails(
        presentAlert: true,
        presentBadge: true,
        presentSound: true,
        categoryIdentifier: 'watering',
      ),
    );
  }

  /// Build alert notification details
  NotificationDetails _buildAlertNotificationDetails() {
    return NotificationDetails(
      android: AndroidNotificationDetails(
        alertChannelId,
        'IrriGo Alert',
        channelDescription: 'Alert dan warning',
        importance: Importance.high,
        priority: Priority.high,
        playSound: true,
        enableVibration: true,
        enableLights: true,
        icon: '@mipmap/ic_launcher',
        autoCancel: true,
        showWhen: true,
      ),
      iOS: const DarwinNotificationDetails(
        presentAlert: true,
        presentBadge: true,
        presentSound: true,
      ),
    );
  }

  /// Build basic notification details
  NotificationDetails _buildBasicNotificationDetails() {
    return NotificationDetails(
      android: AndroidNotificationDetails(
        infoChannelId,
        'IrriGo Informasi',
        channelDescription: 'Informasi sistem',
        importance: Importance.low,
        priority: Priority.low,
        playSound: false,
        enableVibration: false,
        icon: '@mipmap/ic_launcher',
        autoCancel: true,
      ),
      iOS: const DarwinNotificationDetails(
        presentAlert: false,
        presentBadge: true,
        presentSound: false,
      ),
    );
  }

  /// Build automation notification details (WITH SOUND!) - untuk notifikasi otomasi penyiraman
  NotificationDetails _buildAutomationNotificationDetails() {
    return NotificationDetails(
      android: AndroidNotificationDetails(
        wateringChannelId,
        'IrriGo Penyiraman',
        channelDescription: 'Notifikasi penyiraman otomatis',
        importance: Importance.high,
        priority: Priority.high,
        playSound: true,
        enableVibration: true,
        vibrationPattern: Int64List.fromList([0, 500, 200, 500]),
        enableLights: true,
        icon: '@mipmap/ic_launcher',
        largeIcon: const DrawableResourceAndroidBitmap('@mipmap/ic_launcher'),
        autoCancel: true,
        showWhen: true,
        tag: 'automation',
      ),
      iOS: const DarwinNotificationDetails(
        presentAlert: true,
        presentBadge: true,
        presentSound: true,
        categoryIdentifier: 'automation',
      ),
    );
  }

  /// Show instant notification
  Future<void> showInstantNotification({
    required String title,
    required String body,
    String? payload,
    bool isWatering = false,
  }) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    // Check permission before showing
    if (!_hasNotificationPermission) {
      debugPrint(
        '⚠️ Cannot show notification - POST_NOTIFICATIONS permission not granted',
      );
      return;
    }

    try {
      final details =
          isWatering
              ? _buildWateringNotificationDetails()
              : _buildBasicNotificationDetails();

      await _plugin.show(_nextId(), title, body, details, payload: payload);

      debugPrint('✅ Notification: "$title"');
    } catch (e) {
      debugPrint('❌ Error: $e');
    }
  }

  /// Show watering notification (PENTING UNTUK PENYIRAMAN!)
  Future<void> showWateringNotification({
    required String title,
    required String body,
    Map<String, dynamic>? payload,
  }) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    // 🔴 CRITICAL CHECK: Don't continue if permission denied!
    if (!_hasNotificationPermission) {
      debugPrint(
        '🔴 BLOCKED: Cannot show watering notification - POST_NOTIFICATIONS permission not granted!',
      );
      debugPrint(
        '   User may have denied permission when prompted at startup.',
      );
      return;
    }

    try {
      await _plugin.show(
        _nextId(),
        title,
        body,
        _buildWateringNotificationDetails(),
        payload: payload == null ? null : jsonEncode(payload),
      );

      debugPrint('✅ Watering notification: "$title" - "$body"');
    } catch (e) {
      debugPrint('❌ Error showing watering notification: $e');
    }
  }

  /// Show alert notification
  Future<void> showAlertNotification({
    required String title,
    required String body,
    String? payload,
  }) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    // Check permission before showing
    if (!_hasNotificationPermission) {
      debugPrint(
        '⚠️ Cannot show alert notification - POST_NOTIFICATIONS permission not granted',
      );
      return;
    }

    try {
      await _plugin.show(
        _nextId(),
        title,
        body,
        _buildAlertNotificationDetails(),
        payload: payload,
      );

      debugPrint('✅ Alert notification: "$title"');
    } catch (e) {
      debugPrint('❌ Error: $e');
    }
  }

  /// Show mode changed notification
  Future<void> showModeChangedNotification({
    required String modeLabel,
    required bool enabled,
  }) async {
    final body =
        enabled
            ? 'Mode diubah menjadi penyiraman $modeLabel.'
            : 'Mode penyiraman $modeLabel dinonaktifkan.';

    await showInstantNotification(
      title: 'Mode Penyiraman',
      body: body,
      payload: jsonEncode({'type': 'mode_change', 'mode': modeLabel}),
    );
  }

  /// 🆕 Show automation notification dengan deskripsi lengkap (DENGAN BUNYI!)
  /// Digunakan untuk notifikasi otomasi penyiraman sensor
  /// Format sesuai dengan gambar: Title + Full Description (2-3 baris)
  Future<void> showAutomationNotification({
    required String title,
    required String description,
    Map<String, dynamic>? payload,
  }) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    // Check permission before showing
    if (!_hasNotificationPermission) {
      debugPrint(
        '⚠️ Cannot show automation notification - POST_NOTIFICATIONS permission not granted',
      );
      return;
    }

    try {
      await _plugin.show(
        _nextId(),
        title,
        description,
        _buildAutomationNotificationDetails(),
        payload: payload == null ? null : jsonEncode(payload),
      );

      debugPrint('✅ Automation notification: "$title" - "$description"');
    } catch (e) {
      debugPrint('❌ Error showing automation notification: $e');
    }
  }

  /// 🆕 Show schedule changed notification dengan bunyi
  /// Digunakan ketika jadwal penyiraman berubah
  Future<void> showScheduleChangedNotification({
    required String scheduleId,
    required String jadwalWaktu,
    required bool enabled,
    List<int>? potAktif,
  }) async {
    final status = enabled ? 'aktif' : 'nonaktif';
    final potList =
        potAktif != null && potAktif.isNotEmpty
            ? potAktif.join(', ')
            : 'semua pot';

    final title = 'Jadwal Penyiraman Diubah';
    final body =
        'Jadwal $scheduleId ($jadwalWaktu) sekarang $status\nPot: $potList';

    await showAutomationNotification(
      title: title,
      description: body,
      payload: {
        'type': 'schedule_change',
        'scheduleId': scheduleId,
        'enabled': enabled,
        'jadwalWaktu': jadwalWaktu,
      },
    );
  }

  /// 🆕 Show sensor triggered watering notification
  /// Digunakan untuk notifikasi penyiraman otomatis yang dipicu sensor
  Future<void> showSensorWateringNotification({
    required String potName,
    required int currentMoisture,
    required int thresholdMin,
    required int thresholdMax,
    String? additionalInfo,
  }) async {
    final title = 'IrriGo - Penyiraman Otomatis';
    final body =
        StringBuffer()
          ..write('Penyiraman dilakukan pada $potName\n')
          ..write('Kelembaban: $currentMoisture% ');

    if (currentMoisture < thresholdMin) {
      body.write('(di bawah ambang batas $thresholdMin%)');
    } else if (currentMoisture > thresholdMax) {
      body.write('(melebihi ambang batas maksimal $thresholdMax%)');
    }

    if (additionalInfo != null && additionalInfo.isNotEmpty) {
      body.write('\n$additionalInfo');
    }

    await showAutomationNotification(
      title: title,
      description: body.toString(),
      payload: {
        'type': 'sensor_triggered',
        'potName': potName,
        'moisture': currentMoisture,
        'thresholdMin': thresholdMin,
        'thresholdMax': thresholdMax,
      },
    );
  }

  /// 🆕 Show watering execution notification
  /// Digunakan untuk notifikasi saat penyiraman sedang berjalan
  Future<void> showWateringExecutionNotification({
    required String scheduleId,
    required String scheduleWaktu,
    required List<int> potsAktif,
    int? durationSeconds,
  }) async {
    final potList = potsAktif.join(', ');
    final durationText =
        durationSeconds != null ? ' ($durationSeconds detik)' : '';

    final title = 'Penyiraman Dimulai';
    final body =
        'Jadwal: $scheduleId ($scheduleWaktu)\nPot aktif: $potList$durationText';

    await showWateringNotification(
      title: title,
      body: body,
      payload: {
        'type': 'watering_execution',
        'scheduleId': scheduleId,
        'scheduleWaktu': scheduleWaktu,
        'potsAktif': potsAktif,
        'durationSeconds': durationSeconds,
      },
    );
  }

  /// 🆕 Show mode automation enabled notification
  /// Digunakan saat automatic mode diaktifkan
  Future<void> showModeAutomationEnabledNotification({
    required String modeType,
    required String modeDescription,
  }) async {
    final title = 'Mode Penyiraman';
    final body =
        'Mode diubah menjadi penyiraman $modeType berbasis $modeDescription.';

    await showAutomationNotification(
      title: title,
      description: body,
      payload: {
        'type': 'mode_automation',
        'modeType': modeType,
        'modeDescription': modeDescription,
      },
    );
  }

  /// ==================== SCHEDULING ====================

  /// Schedule notification dengan retry logic
  Future<void> scheduleNotification({
    required String title,
    required String body,
    required Duration delay,
    String? payload,
    int retryCount = 3,
  }) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    // 🔴 CRITICAL CHECK: Don't continue if permission denied!
    if (!_hasNotificationPermission) {
      debugPrint(
        '🔴 BLOCKED: Cannot schedule notification - POST_NOTIFICATIONS permission not granted!',
      );
      return;
    }

    for (int attempt = 1; attempt <= retryCount; attempt++) {
      try {
        final scheduledDate = tz.TZDateTime.now(tz.local).add(delay);
        final androidPlugin =
            _plugin
                .resolvePlatformSpecificImplementation<
                  AndroidFlutterLocalNotificationsPlugin
                >();

        final canScheduleExact =
            await androidPlugin?.canScheduleExactNotifications() ?? false;

        // 🔥 3-TIER FALLBACK (from working project):
        // Priority 1: alarmClock (best for background) ← NEW!
        // Priority 2: exactAllowWhileIdle (standard)
        // Priority 3: inexactAllowWhileIdle (fallback)
        final primaryMode =
            canScheduleExact
                ? AndroidScheduleMode
                    .alarmClock // ✅ UPGRADED
                : AndroidScheduleMode.inexactAllowWhileIdle;

        try {
          await _plugin.zonedSchedule(
            _nextId(),
            title,
            body,
            scheduledDate,
            _buildWateringNotificationDetails(),
            androidScheduleMode: primaryMode,
            uiLocalNotificationDateInterpretation:
                UILocalNotificationDateInterpretation.absoluteTime,
            payload: payload,
          );

          debugPrint(
            '✅ Scheduled: "$title" in ${delay.inSeconds}s (${primaryMode.toString().split('.').last} mode)',
          );
          return;
        } on PlatformException catch (e) {
          // Fallback to exactAllowWhileIdle
          if (e.code == 'exact_alarms_not_permitted') {
            debugPrint('  ⚠️ Fallback #1: Trying exactAllowWhileIdle...');
            try {
              await _plugin.zonedSchedule(
                _nextId(),
                title,
                body,
                scheduledDate,
                _buildWateringNotificationDetails(),
                androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
                uiLocalNotificationDateInterpretation:
                    UILocalNotificationDateInterpretation.absoluteTime,
                payload: payload,
              );
              debugPrint('✅ Scheduled with exactAllowWhileIdle fallback');
              return;
            } on PlatformException catch (_) {
              // Final fallback to inexactAllowWhileIdle
              await _plugin.zonedSchedule(
                _nextId(),
                title,
                body,
                scheduledDate,
                _buildWateringNotificationDetails(),
                androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
                uiLocalNotificationDateInterpretation:
                    UILocalNotificationDateInterpretation.absoluteTime,
                payload: payload,
              );
              debugPrint('✅ Scheduled with inexactAllowWhileIdle fallback');
              return;
            }
          }
          rethrow;
        }
      } catch (e) {
        debugPrint('⚠️ Attempt $attempt/$retryCount failed: $e');
        if (attempt < retryCount) {
          await Future.delayed(Duration(milliseconds: 500 * attempt));
        } else {
          debugPrint('🔴 Failed after $retryCount attempts: $e');
        }
      }
    }
  }

  /// ⭐ STABLE 30-SECOND NOTIFICATION - GUARANTEED DELIVERY
  /// Used for testing and reliable short-term notifications
  /// Works even when app minimized or killed!
  Future<bool> scheduleStable30SecondNotification({
    required String title,
    required String body,
    String? payload,
  }) async {
    if (kIsWeb) return false;
    if (!_isInitialized) await initialize();

    debugPrint('\n╔════════════════════════════════════════════╗');
    debugPrint('║  🔔 SCHEDULING 30-SECOND NOTIFICATION ║');
    debugPrint('╚════════════════════════════════════════════╝\n');

    // Pre-check: Verify permission with explicit check (not cached)
    final androidPlugin =
        _plugin
            .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin
            >();

    debugPrint('📋 STAGE 1: Permission Verification');
    try {
      // Request POST_NOTIFICATIONS if needed
      final notifPermGranted =
          await androidPlugin?.requestNotificationsPermission() ?? false;
      debugPrint(
        notifPermGranted
            ? '  ✅ POST_NOTIFICATIONS: GRANTED'
            : '  ❌ POST_NOTIFICATIONS: DENIED (notifications won\'t show!)',
      );

      if (!notifPermGranted) {
        debugPrint(
          '🔴 ABORT: Cannot schedule - notification permission not granted',
        );
        return false;
      }
    } catch (e) {
      debugPrint('  ⚠️ Permission check error: $e');
    }

    debugPrint('\n📋 STAGE 2: Exact Alarm Enforcement');

    // Check and REQUEST exact alarm to maximize reliability
    bool canScheduleExact =
        await androidPlugin?.canScheduleExactNotifications() ?? false;
    debugPrint(
      '  • Current exact alarm capability: ${canScheduleExact ? 'YES' : 'NO'}',
    );

    if (!canScheduleExact) {
      debugPrint(
        '  🔔 Attempting to request SCHEDULE_EXACT_ALARM permission...',
      );
      try {
        canScheduleExact =
            await androidPlugin?.requestExactAlarmsPermission() ?? false;
        debugPrint(
          '  ${canScheduleExact ? '✅' : '⚠️'} After request: ${canScheduleExact ? 'GRANTED' : 'DENIED'}',
        );
      } catch (e) {
        debugPrint('  ⚠️ Exact alarm request error: $e');
      }
    } else {
      debugPrint('  ✅ Exact alarm already available');
    }

    debugPrint('\n📋 STAGE 3: Scheduling with 3-LEVEL FALLBACK (from TelurKu)');

    // 🔥 3-LEVEL FALLBACK STRATEGY (adapted from working TelurKu app):
    // Priority 1: alarmClock (best for background delivery on Android 12+)
    // Priority 2: exactAllowWhileIdle (standard exact alarm)
    // Priority 3: inexactAllowWhileIdle (fallback for devices without exact support)

    final preferredScheduleMode =
        canScheduleExact
            ? AndroidScheduleMode
                .alarmClock // ← UPGRADED: Use alarmClock first!
            : AndroidScheduleMode.inexactAllowWhileIdle;

    debugPrint('  Primary schedule mode: ${preferredScheduleMode.toString()}');

    // Attempt scheduling with exponential retry
    for (int attempt = 1; attempt <= 5; attempt++) {
      try {
        final now = tz.TZDateTime.now(tz.local);
        final scheduledDate = now.add(const Duration(seconds: 30));

        debugPrint(
          '  Attempt $attempt: Scheduling for ${scheduledDate.hour}:${scheduledDate.minute}:${scheduledDate.second}',
        );

        final notificationId = _nextId();

        // CRITICAL: Ensure notification channel exists BEFORE scheduling
        await _setupNotificationChannels();
        debugPrint('  📢 Channel setup verification complete');

        // Log pending before scheduling
        final pendingBefore = await _plugin.pendingNotificationRequests();
        debugPrint('  Pending BEFORE schedule: ${pendingBefore.length}');

        try {
          await _plugin.zonedSchedule(
            notificationId,
            title,
            body,
            scheduledDate,
            _buildScheduledWateringNotificationDetails(),
            androidScheduleMode: preferredScheduleMode,
            uiLocalNotificationDateInterpretation:
                UILocalNotificationDateInterpretation.absoluteTime,
            payload: payload,
          );

          debugPrint(
            '  ✅ SCHEDULED SUCCESSFULLY with ${preferredScheduleMode.toString()}',
          );
        } on PlatformException catch (e) {
          // Handle exact alarm permission error with fallback
          if (e.code == 'exact_alarms_not_permitted') {
            debugPrint(
              '  ⚠️ Exact alarm not permitted, falling back to exactAllowWhileIdle...',
            );

            try {
              await _plugin.zonedSchedule(
                notificationId,
                title,
                body,
                scheduledDate,
                _buildScheduledWateringNotificationDetails(),
                androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
                uiLocalNotificationDateInterpretation:
                    UILocalNotificationDateInterpretation.absoluteTime,
                payload: payload,
              );

              debugPrint('  ✅ SCHEDULED SUCCESSFULLY with exactAllowWhileIdle');
            } on PlatformException catch (e2) {
              // Final fallback to inexact
              debugPrint(
                '  ⚠️ exactAllowWhileIdle also failed ($e2), using inexactAllowWhileIdle...',
              );

              await _plugin.zonedSchedule(
                notificationId,
                title,
                body,
                scheduledDate,
                _buildScheduledWateringNotificationDetails(),
                androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
                uiLocalNotificationDateInterpretation:
                    UILocalNotificationDateInterpretation.absoluteTime,
                payload: payload,
              );

              debugPrint(
                '  ✅ SCHEDULED SUCCESSFULLY with inexactAllowWhileIdle (final fallback)',
              );
            }
          } else {
            rethrow;
          }
        }

        debugPrint('  Notification ID: $notificationId');
        debugPrint('  Scheduled time: ${scheduledDate.toString()}');
        debugPrint(
          '  Mode: ${canScheduleExact ? '⚡ ALARMCLOCK (best for background)' : '⏳ INEXACT (may delay on idle device)'}',
        );

        // Verify notification was added to pending list
        await Future.delayed(const Duration(milliseconds: 500));
        final pending = await _plugin.pendingNotificationRequests();
        final isInPending = pending.any((p) => p.id == notificationId);
        debugPrint(
          '\n✅ Verification: Notification ${isInPending ? 'IS' : 'NOT'} in pending list',
        );
        debugPrint('  Total pending notifications: ${pending.length}');
        if (isInPending) {
          final pendingNotif = pending.firstWhere(
            (p) => p.id == notificationId,
          );
          debugPrint(
            '  Pending notif: ${pendingNotif.title} (ID: ${pendingNotif.id})',
          );
        }

        debugPrint('\n💡 WHAT TO DO NEXT:');
        debugPrint('  1️⃣ Wait 30 seconds OR minimize app/go home/kill app');
        debugPrint('  2️⃣ Notification should appear from notification area');
        debugPrint('  3️⃣ If no notification: Check app notification settings');
        debugPrint('\n💡 TROUBLESHOOTING:');
        debugPrint(
          '  • If notification is in pending list but doesn\'t appear:',
        );
        debugPrint('    → Check MIUI/ColorOS/OneUI battery optimization');
        debugPrint(
          '    → Settings → Battery → Background restrictions → IrriGo',
        );
        debugPrint('    → App should be in "Whitelist" or "Don\'t optimize"');
        debugPrint('\n╚════════════════════════════════════════════╝\n');

        return true;
      } catch (e) {
        debugPrint('  ❌ Attempt $attempt failed: $e');
        if (attempt < 5) {
          final delayMs = 200 * attempt;
          debugPrint('  ⏳ Retrying in ${delayMs}ms...');
          await Future.delayed(Duration(milliseconds: delayMs));
        }
      }
    }

    debugPrint('🔴 FAILED: Could not schedule notification after 5 attempts');
    debugPrint('╚════════════════════════════════════════════╝\n');
    return false;
  }

  /// Schedule daily notification
  Future<void> scheduleDailyNotificationAtTime({
    required String scheduleKey,
    required String title,
    required String body,
    required int hour,
    required int minute,
    String? payload,
  }) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    try {
      final now = tz.TZDateTime.now(tz.local);
      var scheduled = tz.TZDateTime(
        tz.local,
        now.year,
        now.month,
        now.day,
        hour,
        minute,
      );

      if (scheduled.isBefore(now)) {
        scheduled = scheduled.add(const Duration(days: 1));
      }

      final androidPlugin =
          _plugin
              .resolvePlatformSpecificImplementation<
                AndroidFlutterLocalNotificationsPlugin
              >();
      final canScheduleExact =
          await androidPlugin?.canScheduleExactNotifications() ?? false;

      // 🔥 3-TIER FALLBACK STRATEGY (From Working TelurKu):
      // Priority 1: alarmClock (wakes device, fires even in deep sleep) ← CRITICAL!
      // Priority 2: exactAllowWhileIdle (fires during doze window)
      // Priority 3: inexactAllowWhileIdle (unreliable, major delay possible)

      final primaryMode =
          canScheduleExact
              ? AndroidScheduleMode
                  .alarmClock // ✅ UPGRADED: Primary for background
              : AndroidScheduleMode.inexactAllowWhileIdle;

      try {
        await _plugin.zonedSchedule(
          _stableIdFromKey(scheduleKey),
          title,
          body,
          scheduled,
          _buildWateringNotificationDetails(),
          androidScheduleMode: primaryMode,
          uiLocalNotificationDateInterpretation:
              UILocalNotificationDateInterpretation.absoluteTime,
          matchDateTimeComponents: DateTimeComponents.time,
          payload: payload,
        );

        debugPrint(
          '✅ Daily scheduled at $hour:${minute.toString().padLeft(2, '0')} with ${primaryMode.toString().split('.').last}',
        );
      } on PlatformException catch (e) {
        // Fallback to exactAllowWhileIdle if alarmClock fails
        if (e.code == 'exact_alarms_not_permitted') {
          debugPrint(
            '⚠️ alarmClock not permitted, falling back to exactAllowWhileIdle...',
          );
          try {
            await _plugin.zonedSchedule(
              _stableIdFromKey(scheduleKey),
              title,
              body,
              scheduled,
              _buildWateringNotificationDetails(),
              androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
              uiLocalNotificationDateInterpretation:
                  UILocalNotificationDateInterpretation.absoluteTime,
              matchDateTimeComponents: DateTimeComponents.time,
              payload: payload,
            );
            debugPrint('✅ Scheduled with exactAllowWhileIdle fallback');
          } on PlatformException catch (_) {
            // Final fallback to inexactAllowWhileIdle
            await _plugin.zonedSchedule(
              _stableIdFromKey(scheduleKey),
              title,
              body,
              scheduled,
              _buildWateringNotificationDetails(),
              androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
              uiLocalNotificationDateInterpretation:
                  UILocalNotificationDateInterpretation.absoluteTime,
              matchDateTimeComponents: DateTimeComponents.time,
              payload: payload,
            );
            debugPrint('✅ Scheduled with inexactAllowWhileIdle final fallback');
          }
        } else {
          rethrow;
        }
      }
    } catch (e) {
      debugPrint('❌ Error: $e');
    }
  }

  /// Cancel notification
  Future<void> cancelScheduledNotification(String scheduleKey) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    try {
      await _plugin.cancel(_stableIdFromKey(scheduleKey));
      debugPrint('✅ Cancelled: $scheduleKey');
    } catch (e) {
      debugPrint('⚠️ Error: $e');
    }
  }

  /// Sync schedule reminders from Firebase
  Future<void> syncScheduleRemindersFromFirebase() async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    debugPrint('📡 Syncing schedules from Firebase...');

    final db = FirebaseDatabase.instance.ref();
    await _syncFromKontrolNode(db.child('kontrol'), 'kontrol');
    await _syncFromKontrolNode(db.child('kontrol_1'), 'kontrol_1');
  }

  /// ⭐ NEW: Schedule watering execution for all active schedules
  /// This is the main method to use for scheduling watering without worker
  Future<void> scheduleAllWateringExecutions() async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    debugPrint('⏰ Scheduling all watering executions from Firebase...');

    final db = FirebaseDatabase.instance.ref();

    // Try both kontrol and kontrol_1 paths
    for (final path in ['kontrol', 'kontrol_1']) {
      await _scheduleWateringFromKontrolNode(db.child(path), path);
    }
  }

  /// Helper to schedule watering from a kontrol node
  Future<void> _scheduleWateringFromKontrolNode(
    DatabaseReference ref,
    String nodeName,
  ) async {
    try {
      final snapshot = await ref.get();
      if (!snapshot.exists || snapshot.value is! Map) {
        debugPrint('⏰ No data found for $nodeName');
        return;
      }

      final data = Map<String, dynamic>.from(snapshot.value as Map);
      final waktuEnabled = data['waktu'] == true;

      if (!waktuEnabled) {
        debugPrint('⏰ Waktu mode is disabled for $nodeName');
        return;
      }

      final scheduleKeys =
          data.keys.where((key) => key.startsWith('jadwal_')).toList()..sort();

      debugPrint(
        '⏰ Found ${scheduleKeys.length} jadwal candidates in $nodeName: $scheduleKeys',
      );

      int scheduledCount = 0;
      for (final key in scheduleKeys) {
        final scheduleDataRaw = data[key];
        if (scheduleDataRaw is! Map) {
          debugPrint('⏰ Skipping $key: not a Map');
          continue;
        }

        final scheduleData = Map<String, dynamic>.from(scheduleDataRaw);
        final aktif = scheduleData['aktif'] != false;
        final waktu = (scheduleData['waktu'] ?? '').toString();
        final isValidTime = RegExp(
          r'^([01]\d|2[0-3]):[0-5]\d$',
        ).hasMatch(waktu);

        debugPrint(
          '⏰ $key: aktif=$aktif, waktu=$waktu, isValidTime=$isValidTime',
        );

        if (!aktif || !isValidTime) {
          debugPrint('⏰ Skipping $key: aktif=$aktif, validTime=$isValidTime');
          continue;
        }

        final parts = waktu.split(':');
        final hour = int.parse(parts[0]);
        final minute = int.parse(parts[1]);

        // Schedule watering execution (not just notification reminder!)
        await _scheduleWateringExecutionAtTime(
          scheduleKey: '$nodeName:$key',
          kontrolPath: nodeName,
          scheduleId: key,
          hour: hour,
          minute: minute,
          scheduleData: scheduleData,
        );
        scheduledCount++;
      }

      debugPrint(
        '✅ Successfully scheduled $scheduledCount watering executions from $nodeName (total candidates: ${scheduleKeys.length})',
      );
    } catch (e) {
      debugPrint('❌ Error scheduling watering from $nodeName: $e');
    }
  }

  /// Schedule watering execution at specific time
  /// This actually executes the watering, not just sends notification
  Future<void> _scheduleWateringExecutionAtTime({
    required String scheduleKey,
    required String kontrolPath,
    required String scheduleId,
    required int hour,
    required int minute,
    required Map<String, dynamic> scheduleData,
  }) async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    try {
      final now = tz.TZDateTime.now(tz.local);
      var scheduled = tz.TZDateTime(
        tz.local,
        now.year,
        now.month,
        now.day,
        hour,
        minute,
      );

      if (scheduled.isBefore(now)) {
        scheduled = scheduled.add(const Duration(days: 1));
      }

      final androidPlugin =
          _plugin
              .resolvePlatformSpecificImplementation<
                AndroidFlutterLocalNotificationsPlugin
              >();
      final canScheduleExact =
          await androidPlugin?.canScheduleExactNotifications() ?? false;

      // Build payload with full schedule data for execution
      final payload = jsonEncode({
        'type': 'watering_execution',
        'scheduleKey': scheduleKey,
        'kontrolPath': kontrolPath,
        'scheduleId': scheduleId,
        'pompa_air': scheduleData['pompa_air'] ?? true,
        'pompa_pupuk': scheduleData['pompa_pupuk'] ?? false,
        'pompa_pengaduk': scheduleData['pompa_pengaduk'] ?? false,
        'pot_aktif': scheduleData['pot_aktif'] ?? [1, 2, 3, 4, 5],
        'durasi': scheduleData['durasi'] ?? 60,
      });

      // Build title dan body dengan format lengkap (SESUAI GAMBAR!)
      final title = 'IrriGo - Penyiraman Terjadwal';
      final potAktif =
          (scheduleData['pot_aktif'] as List?)?.cast<int>().join(', ') ??
          'semua pot';
      final durasiDetik = scheduleData['durasi'] ?? 60;
      final body =
          'Jadwal: ${scheduleId.replaceFirst('jadwal_', '')}\n'
          'Waktu: ${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}\n'
          'Pot: $potAktif ($durasiDetik detik)';

      final notifId = _stableIdFromKey(scheduleKey);

      // 🔥 3-TIER FALLBACK STRATEGY:
      // Priority 1: alarmClock (wakes device for watering!) ← CRITICAL!
      // Priority 2: exactAllowWhileIdle (standard exact alarm)
      // Priority 3: inexactAllowWhileIdle (fallback for devices without exact)

      final primaryMode =
          canScheduleExact
              ? AndroidScheduleMode
                  .alarmClock // ✅ UPGRADED: Primary
              : AndroidScheduleMode.inexactAllowWhileIdle;

      try {
        await _plugin.zonedSchedule(
          notifId,
          title,
          body,
          scheduled,
          _buildScheduledWateringNotificationDetails(),
          androidScheduleMode: primaryMode,
          uiLocalNotificationDateInterpretation:
              UILocalNotificationDateInterpretation.absoluteTime,
          matchDateTimeComponents: DateTimeComponents.time,
          payload: payload,
        );

        debugPrint(
          '⏰ ✅ Scheduled $scheduleId: ID=$notifId, time=${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}, mode=${primaryMode.toString()}',
        );
        debugPrint('   Next fire: ${scheduled.toString()}');
        debugPrint('   Payload keys: ${jsonDecode(payload).keys.toString()}');
      } on PlatformException catch (e) {
        // Fallback 1: exactAllowWhileIdle
        if (e.code == 'exact_alarms_not_permitted') {
          debugPrint(
            '⚠️ alarmClock not permitted for $scheduleId, trying exactAllowWhileIdle...',
          );
          try {
            await _plugin.zonedSchedule(
              notifId,
              title,
              body,
              scheduled,
              _buildScheduledWateringNotificationDetails(),
              androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
              uiLocalNotificationDateInterpretation:
                  UILocalNotificationDateInterpretation.absoluteTime,
              matchDateTimeComponents: DateTimeComponents.time,
              payload: payload,
            );
            debugPrint('⏰ ✅ Scheduled $scheduleId with exactAllowWhileIdle');
          } on PlatformException catch (_) {
            // Fallback 2: inexactAllowWhileIdle
            await _plugin.zonedSchedule(
              notifId,
              title,
              body,
              scheduled,
              _buildScheduledWateringNotificationDetails(),
              androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
              uiLocalNotificationDateInterpretation:
                  UILocalNotificationDateInterpretation.absoluteTime,
              matchDateTimeComponents: DateTimeComponents.time,
              payload: payload,
            );
            debugPrint('⏰ ✅ Scheduled $scheduleId with inexactAllowWhileIdle');
          }
        } else {
          rethrow;
        }
      }
    } catch (e) {
      debugPrint('❌ Error scheduling watering execution for $scheduleId: $e');
    }
  }

  /// Cancel all watering schedule executions
  Future<void> cancelAllWateringSchedules() async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    try {
      final pending = await _plugin.pendingNotificationRequests();

      // Cancel only watering execution schedules (those that start with kontrol:jadwal)
      final wateringSchedules =
          pending.where((p) {
            return p.payload != null &&
                p.payload!.contains('watering_execution');
          }).toList();

      for (final schedule in wateringSchedules) {
        await _plugin.cancel(schedule.id);
      }

      debugPrint('✅ Cancelled ${wateringSchedules.length} watering schedules');
    } catch (e) {
      debugPrint('❌ Error cancelling watering schedules: $e');
    }
  }

  /// ⭐ Check and execute pending watering schedules
  /// Call this when app resumes to ensure watering executes
  /// even if user didn't tap notification while app was backgrounded
  Future<void> checkAndExecutePendingWaterings() async {
    if (kIsWeb) return;
    if (!_isInitialized) await initialize();

    try {
      debugPrint(
        '🔔 Checking pending watering schedules (call when app resumes)...',
      );

      final pending = await _plugin.pendingNotificationRequests();
      debugPrint('📋 Total pending notifications: ${pending.length}');

      // Find watering_execution type notifications
      final wateringNotifications =
          pending.where((p) {
            if (p.payload == null) return false;
            try {
              final data = jsonDecode(p.payload!) as Map<String, dynamic>;
              return data['type'] == 'watering_execution';
            } catch (_) {
              return false;
            }
          }).toList();

      debugPrint(
        '💧 Found ${wateringNotifications.length} pending watering notifications',
      );

      // Execute each pending watering
      for (final notif in wateringNotifications) {
        try {
          final data = jsonDecode(notif.payload!) as Map<String, dynamic>;
          final scheduleId = (data['scheduleId'] ?? 'unknown') as String;

          debugPrint(
            '   📌 Executing pending watering: $scheduleId (notif ID: ${notif.id})',
          );

          // Execute via callback atau direct
          if (_onWateringScheduleCallback != null) {
            // Execute via callback
            await Future.sync(() => _onWateringScheduleCallback?.call(data));
            debugPrint('   ✅ Executed via callback');
          } else {
            debugPrint('   ⚠️ No callback registered, skipping execution');
          }

          // Cancel notification setelah execute (optional, untuk clean state)
          // await _plugin.cancel(notif.id);
        } catch (e) {
          debugPrint('   ❌ Error executing watering: $e');
        }
      }

      if (wateringNotifications.isNotEmpty) {
        debugPrint(
          '✅ Checked and processed ${wateringNotifications.length} pending watering notifications',
        );
      }
    } catch (e) {
      debugPrint('❌ Error checking pending waterings: $e');
    }
  }

  Future<void> _syncFromKontrolNode(
    DatabaseReference ref,
    String nodeName,
  ) async {
    try {
      final snapshot = await ref.get();
      if (!snapshot.exists || snapshot.value is! Map) {
        return;
      }

      final data = Map<String, dynamic>.from(snapshot.value as Map);
      final waktuEnabled = data['waktu'] == true;

      final scheduleKeys =
          data.keys.where((key) => key.startsWith('jadwal_')).toList()..sort();

      for (final key in scheduleKeys) {
        final scheduleKey = '$nodeName:$key';
        final scheduleDataRaw = data[key];
        if (scheduleDataRaw is! Map) {
          await cancelScheduledNotification(scheduleKey);
          continue;
        }

        final scheduleData = Map<String, dynamic>.from(scheduleDataRaw);
        final aktif = scheduleData['aktif'] != false;
        final waktu = (scheduleData['waktu'] ?? '').toString();
        final isValidTime = RegExp(
          r'^([01]\d|2[0-3]):[0-5]\d$',
        ).hasMatch(waktu);

        if (!waktuEnabled || !aktif || !isValidTime) {
          await cancelScheduledNotification(scheduleKey);
          continue;
        }

        final parts = waktu.split(':');
        final hour = int.parse(parts[0]);
        final minute = int.parse(parts[1]);

        await scheduleDailyNotificationAtTime(
          scheduleKey: scheduleKey,
          title: 'IrriGo - Jadwal Penyiraman',
          body: 'Penyiraman akan dimulai pada jam $waktu',
          hour: hour,
          minute: minute,
          payload: jsonEncode({
            'type': 'local_schedule',
            'key': key,
            'time': waktu,
          }),
        );
      }
    } catch (e) {
      debugPrint('❌ Error syncing $nodeName: $e');
    }
  }

  /// ==================== FIREBASE & FCM ====================

  Future<String?> getFcmToken() async {
    if (kIsWeb) return null;
    if (!_isInitialized) await initialize();

    try {
      final token = await FirebaseMessaging.instance.getToken();
      return token;
    } catch (e) {
      debugPrint('❌ FCM token error: $e');
      return null;
    }
  }

  Future<void> _showRemoteMessage(RemoteMessage message) async {
    final notification = message.notification;
    final title = notification?.title ?? 'IrriGo';
    final body = notification?.body ?? '';

    if (body.isEmpty) return;

    await showInstantNotification(
      title: title,
      body: body,
      payload: jsonEncode(message.data),
      isWatering: body.toLowerCase().contains('penyiraman'),
    );
  }

  /// ==================== UTILITIES ====================

  /// Check if notification ID is still in pending list
  Future<bool> isNotificationPending(int notificationId) async {
    if (kIsWeb) return false;
    if (!_isInitialized) await initialize();

    try {
      final pending = await _plugin.pendingNotificationRequests();
      return pending.any((p) => p.id == notificationId);
    } catch (e) {
      debugPrint('⚠️ Error checking pending: $e');
      return false;
    }
  }

  /// Get detailed diagnostic about a specific notification
  Future<Map<String, dynamic>> getNotificationStatus(int notificationId) async {
    if (kIsWeb) return {};
    if (!_isInitialized) await initialize();

    try {
      final pending = await _plugin.pendingNotificationRequests();
      final matching = pending.where((p) => p.id == notificationId).toList();

      return {
        'id': notificationId,
        'isPending': matching.isNotEmpty,
        'title': matching.isNotEmpty ? matching.first.title : null,
        'body': matching.isNotEmpty ? matching.first.body : null,
        'totalPending': pending.length,
      };
    } catch (e) {
      return {'error': e.toString()};
    }
  }

  Future<List<PendingNotificationRequest>> pendingNotifications() async {
    if (kIsWeb) return <PendingNotificationRequest>[];
    if (!_isInitialized) await initialize();

    try {
      return await _plugin.pendingNotificationRequests();
    } catch (e) {
      debugPrint('⚠️ Error: $e');
      return [];
    }
  }

  Future<void> cancelAll() async {
    if (kIsWeb) return;
    try {
      await _plugin.cancelAll();
      debugPrint('✅ All notifications cancelled');
    } catch (e) {
      debugPrint('⚠️ Error: $e');
    }
  }

  Future<bool> canScheduleExactNotifications() async {
    if (kIsWeb) return false;
    if (!_isInitialized) await initialize();

    final androidPlugin =
        _plugin
            .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin
            >();
    return await androidPlugin?.canScheduleExactNotifications() ?? false;
  }

  Future<bool> requestExactAlarmsPermission() async {
    if (kIsWeb) return false;
    if (!_isInitialized) await initialize();

    final androidPlugin =
        _plugin
            .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin
            >();
    try {
      return await androidPlugin?.requestExactAlarmsPermission() ?? false;
    } on PlatformException {
      return false;
    }
  }

  // ==================== PRIVATE HELPERS ====================

  int _nextId() {
    _notificationCounter = (_notificationCounter + 1) % 100000;
    final nowPart = DateTime.now().millisecondsSinceEpoch % 2000000000;
    final id = nowPart + _notificationCounter;
    return id > _maxAndroidNotificationId ? id - _maxAndroidNotificationId : id;
  }

  int _stableIdFromKey(String key) {
    var hash = 0;
    for (final codeUnit in key.codeUnits) {
      hash = 0x1fffffff & (hash + codeUnit);
      hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
      hash ^= (hash >> 6);
    }
    hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
    hash ^= (hash >> 11);
    hash = 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
    return hash % _maxAndroidNotificationId;
  }

  static void _onNotificationResponse(NotificationResponse response) {
    final payload = response.payload;
    if (payload != null) {
      debugPrint('🔔 Notification response payload: $payload');

      try {
        final data = jsonDecode(payload) as Map<String, dynamic>;
        final type = data['type'] as String?;

        switch (type) {
          case 'sensor_triggered':
            debugPrint('📊 Sensor triggered watering detected');
            break;
          case 'local_schedule':
            debugPrint('🕐 Local schedule notification triggered');
            // Trigger watering execution via callback
            if (_onWateringScheduleCallback != null) {
              debugPrint('🔔 Executing scheduled watering via callback...');
              _onWateringScheduleCallback!(data);
            }
            break;
          case 'watering_execution':
            debugPrint('💧 Watering execution scheduled');
            // Trigger watering execution via callback
            if (_onWateringScheduleCallback != null) {
              debugPrint('🔔 Executing watering via callback...');
              _onWateringScheduleCallback!(data);
            }
            break;
          case 'mode_change':
            debugPrint('🔄 Mode change notification');
            break;
          default:
            debugPrint('ℹ️ Generic notification: $type');
        }
      } catch (e) {
        debugPrint('⚠️ Error parsing payload: $e');
      }
    }
  }

  /// ==================== DIAGNOSTIC ====================

  /// Get diagnostic status of notification system
  Future<Map<String, dynamic>> getDiagnosticStatus() async {
    if (!_isInitialized) await initialize();

    try {
      final androidPlugin =
          _plugin
              .resolvePlatformSpecificImplementation<
                AndroidFlutterLocalNotificationsPlugin
              >();

      final canScheduleExact =
          await androidPlugin?.canScheduleExactNotifications() ?? false;
      final pending = await pendingNotifications();
      final fcmToken = await getFcmToken();

      final status = {
        'initialized': _isInitialized,
        'permission_granted': _hasNotificationPermission,
        'can_schedule_exact': canScheduleExact,
        'pending_notifications': pending.length,
        'has_fcm_token': fcmToken != null && fcmToken.isNotEmpty,
        'fcm_token_length': fcmToken?.length ?? 0,
        'timezone': tz.local.name,
      };

      debugPrint(
        '📊 Notification Diagnostic:\n${status.entries.map((e) => '   ${e.key}: ${e.value}').join('\n')}',
      );

      return status;
    } catch (e) {
      debugPrint('❌ Diagnostic failed: $e');
      return {'error': e.toString()};
    }
  }

  /// Print diagnostic info to logs
  Future<void> printDiagnosticInfo() async {
    if (!_isInitialized) await initialize();

    debugPrint('\n╔════════════════════════════════════════════╗');
    debugPrint('║  🔔 NOTIFICATION SYSTEM DIAGNOSTIC        ║');
    debugPrint('╚════════════════════════════════════════════╝\n');

    debugPrint('✅ Service initialized: $_isInitialized');
    debugPrint(
      _hasNotificationPermission
          ? '✅ Permission granted: YES'
          : '❌ Permission granted: NO',
    );

    final exact = await canScheduleExactNotifications();
    debugPrint(
      exact ? '✅ Exact alarm: AVAILABLE' : '⚠️  Exact alarm: UNAVAILABLE',
    );

    final pending = await pendingNotifications();
    debugPrint('📋 Pending notifications: ${pending.length}');

    final token = await getFcmToken();
    debugPrint(
      token != null && token.isNotEmpty
          ? '✅ FCM Token: ${token.substring(0, 20)}...'
          : '❌ FCM Token: NOT SET',
    );

    debugPrint('\n💡 TROUBLESHOOTING TIPS:');
    if (!_hasNotificationPermission) {
      debugPrint(
        '   - Permission NOT granted. User must grant POST_NOTIFICATIONS',
      );
      debugPrint(
        '   - Check: Settings → Apps → IrriGo → Notifications → Allow',
      );
    }
    if (!exact) {
      debugPrint(
        '   - Exact alarm not available. Scheduling will use inexact (could delay 15+ minutes)',
      );
    }
    if (pending.isEmpty) {
      debugPrint('   - No scheduled notifications pending');
    }

    debugPrint('\n╚════════════════════════════════════════════╝\n');
  }
}
