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

class NotificationService {
  NotificationService._();

  static final NotificationService instance = NotificationService._();

  static const String _channelId = 'telurku_panen_channel';
  static const String _channelName = 'Panen TelurKu';
  static const String _channelDescription =
      'Notifikasi panen dan debug TelurKu';

  final FlutterLocalNotificationsPlugin _plugin =
      FlutterLocalNotificationsPlugin();
  static const int _maxAndroidNotificationId = 2147483647;
  bool _isInitialized = false;
  int _notificationCounter = 0;

  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;
  }

  Future<void> initialize() async {
    if (_isInitialized) return;
    if (kIsWeb) return;

    try {
      tz.initializeTimeZones();
      final localTimeZone = await FlutterTimezone.getLocalTimezone();
      tz.setLocalLocation(tz.getLocation(localTimeZone));

      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,
      );

      await _plugin.initialize(
        initSettings,
        onDidReceiveNotificationResponse: _onNotificationResponse,
      );
      await _setupNotificationChannel();
      await _requestPermissions();

      _isInitialized = true;

      if (kDebugMode) {
        print('✅ NotificationService initialized dengan channel panen');
      }
    } catch (e) {
      if (kDebugMode) {
        print('❌ NotificationService init failed: $e');
      }
      rethrow;
    }
  }

  Future<void> _setupNotificationChannel() async {
    try {
      final androidPlugin = _plugin.resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>();

      const AndroidNotificationChannel channel = AndroidNotificationChannel(
        _channelId,
        _channelName,
        description: _channelDescription,
        importance: Importance.max,
        playSound: true,
        enableLights: true,
        enableVibration: true,
      );

      await androidPlugin?.createNotificationChannel(channel);
      if (kDebugMode) {
        print('✅ Notification channel telurku_panen_channel created');
      }
    } catch (e) {
      if (kDebugMode) {
        print('⚠️ Error setting notification channel: $e');
      }
    }
  }

  Future<void> _requestPermissions() async {
    try {
      final androidPlugin = _plugin.resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>();

      final notifPermission =
          await androidPlugin?.requestNotificationsPermission() ?? false;
      if (kDebugMode) {
        print(
          notifPermission
              ? '✅ Notification permission granted'
              : '⚠️ Notification permission not granted',
        );
      }

      final exactAlarmPermission =
          await androidPlugin?.requestExactAlarmsPermission() ?? false;
      if (kDebugMode) {
        print(exactAlarmPermission
            ? '✅ Exact alarm permission granted'
            : '⚠️ Exact alarm permission not granted');
      }

      await _plugin
          .resolvePlatformSpecificImplementation<
              IOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(alert: true, badge: true, sound: true);
    } catch (e) {
      if (kDebugMode) {
        print('⚠️ Error requesting permissions: $e');
      }
    }
  }

  NotificationDetails _buildNotificationDetails() {
    return const NotificationDetails(
      android: AndroidNotificationDetails(
        _channelId,
        _channelName,
        channelDescription: _channelDescription,
        importance: Importance.max,
        priority: Priority.high,
        playSound: true,
        enableVibration: true,
        icon: '@mipmap/ic_launcher',
      ),
      iOS: DarwinNotificationDetails(
        presentAlert: true,
        presentBadge: true,
        presentSound: true,
      ),
    );
  }

  int _nextId() {
    // Android mewajibkan ID notifikasi dalam rentang signed 32-bit.
    _notificationCounter = (_notificationCounter + 1) % 100000;
    final nowPart = DateTime.now().millisecondsSinceEpoch % 2000000000;
    final id = nowPart + _notificationCounter;
    return id > _maxAndroidNotificationId ? id - _maxAndroidNotificationId : id;
  }

  Future<void> showInstantNotification({
    required String title,
    required String body,
    String? payload,
  }) async {
    if (kIsWeb) return;
    await initialize();
    await _plugin.show(
      _nextId(),
      title,
      body,
      _buildNotificationDetails(),
      payload: payload,
    );
  }

  Future<void> scheduleNotification({
    required String title,
    required String body,
    required Duration delay,
    bool prioritizeBackgroundDelivery = false,
    String? payload,
  }) async {
    if (kIsWeb) return;
    await initialize();

    final scheduledDate = tz.TZDateTime.now(tz.local).add(delay);
    final notificationId = _nextId();
    final androidPlugin = _plugin.resolvePlatformSpecificImplementation<
        AndroidFlutterLocalNotificationsPlugin>();
    final canScheduleExact =
        await androidPlugin?.canScheduleExactNotifications() ?? true;

    if (!canScheduleExact) {
      final permissionGranted =
          await androidPlugin?.requestExactAlarmsPermission() ?? false;

      if (kDebugMode) {
        print(
          permissionGranted
              ? '✅ Exact alarm permission granted'
              : '⚠️ Exact alarm permission not granted, using inexact fallback',
        );
      }
    }

    final useExactSchedule =
        await androidPlugin?.canScheduleExactNotifications() ?? true;
    final preferredAndroidScheduleMode =
        prioritizeBackgroundDelivery && useExactSchedule
            ? AndroidScheduleMode.alarmClock
            : (useExactSchedule
                ? AndroidScheduleMode.exactAllowWhileIdle
                : AndroidScheduleMode.inexactAllowWhileIdle);

    try {
      await _plugin.zonedSchedule(
        notificationId,
        title,
        body,
        scheduledDate,
        _buildNotificationDetails(),
        androidScheduleMode: preferredAndroidScheduleMode,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
        payload: payload,
      );
    } on PlatformException catch (e) {
      if (e.code != 'exact_alarms_not_permitted') {
        rethrow;
      }

      if (kDebugMode) {
        print('⚠️ Exact alarm tidak diizinkan, fallback ke inexact alarm.');
      }

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

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

    final scheduledDate = _nextTimeAt(hour, minute);
    final notificationId = _stableIdFromKey(scheduleKey);
    final androidPlugin = _plugin.resolvePlatformSpecificImplementation<
        AndroidFlutterLocalNotificationsPlugin>();
    final canScheduleExactBeforeRequest =
        await androidPlugin?.canScheduleExactNotifications() ?? true;

    if (!canScheduleExactBeforeRequest) {
      await androidPlugin?.requestExactAlarmsPermission();
    }

    final canScheduleExact =
        await androidPlugin?.canScheduleExactNotifications() ?? true;
    final preferredMode = canScheduleExact
        ? AndroidScheduleMode.alarmClock
        : AndroidScheduleMode.inexactAllowWhileIdle;

    if (kDebugMode) {
      print(
          '⏰ Daily reminder [$scheduleKey] -> ${scheduledDate.toLocal()} (mode: $preferredMode)');
    }

    try {
      await _plugin.zonedSchedule(
        notificationId,
        title,
        body,
        scheduledDate,
        _buildNotificationDetails(),
        androidScheduleMode: preferredMode,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
        matchDateTimeComponents: DateTimeComponents.time,
        payload: payload,
      );
    } on PlatformException catch (e) {
      if (e.code != 'exact_alarms_not_permitted') {
        if (kDebugMode) {
          print('⚠️ Daily reminder primary mode failed: $e');
        }
      }

      try {
        await _plugin.zonedSchedule(
          notificationId,
          title,
          body,
          scheduledDate,
          _buildNotificationDetails(),
          androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
          uiLocalNotificationDateInterpretation:
              UILocalNotificationDateInterpretation.absoluteTime,
          matchDateTimeComponents: DateTimeComponents.time,
          payload: payload,
        );
      } on PlatformException {
        await _plugin.zonedSchedule(
          notificationId,
          title,
          body,
          scheduledDate,
          _buildNotificationDetails(),
          androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
          uiLocalNotificationDateInterpretation:
              UILocalNotificationDateInterpretation.absoluteTime,
          matchDateTimeComponents: DateTimeComponents.time,
          payload: payload,
        );
      }
    }
  }

  Future<void> cancelScheduledNotification(String scheduleKey) async {
    if (kIsWeb) return;
    try {
      final notificationId = _stableIdFromKey(scheduleKey);
      await _plugin.cancel(notificationId);
      if (kDebugMode) {
        print(
            '✅ Cancelled scheduled notification: $scheduleKey (ID: $notificationId)');
      }
    } catch (e) {
      if (kDebugMode) {
        print('⚠️ Error cancelling notification $scheduleKey: $e');
      }
    }
  }

  tz.TZDateTime _nextTimeAt(int hour, int minute) {
    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));
    }
    return scheduled;
  }

  Future<bool> canScheduleExactNotifications() async {
    if (kIsWeb) return false;
    await initialize();
    final androidPlugin = _plugin.resolvePlatformSpecificImplementation<
        AndroidFlutterLocalNotificationsPlugin>();
    return await androidPlugin?.canScheduleExactNotifications() ?? false;
  }

  Future<bool> requestExactAlarmsPermission() async {
    if (kIsWeb) return false;
    await initialize();
    final androidPlugin = _plugin.resolvePlatformSpecificImplementation<
        AndroidFlutterLocalNotificationsPlugin>();
    return await androidPlugin?.requestExactAlarmsPermission() ?? false;
  }

  Future<void> cancelAll() async {
    if (kIsWeb) return;
    await _plugin.cancelAll();
  }

  Future<List<PendingNotificationRequest>> pendingNotifications() async {
    if (kIsWeb) return <PendingNotificationRequest>[];
    return _plugin.pendingNotificationRequests();
  }

  Future<void> showPanenSummaryNotification({
    required String jenisPanen,
    required Map<String, int> kandangTotals,
  }) async {
    final normalizedJenis = jenisPanen.trim().toLowerCase();
    final total =
        kandangTotals.values.fold<int>(0, (sum, value) => sum + value);
    final detailText = kandangTotals.entries
        .map((entry) => '${entry.key} = ${entry.value}')
        .join(', ');

    final title = 'Panen ${_capitalize(normalizedJenis)}';
    final body =
        'Panen ${normalizedJenis.isEmpty ? jenisPanen : normalizedJenis} telah dilakukan, didapatkan total telur $total${detailText.isNotEmpty ? ', $detailText' : ''}';

    await showInstantNotification(
      title: title,
      body: body,
      payload: 'panen:$normalizedJenis',
    );
  }

  static String _capitalize(String value) {
    if (value.isEmpty) return value;
    return value[0].toUpperCase() + value.substring(1);
  }

  static void _onNotificationResponse(NotificationResponse response) {
    if (kDebugMode) {
      print('🔔 Notification tapped: ${response.payload ?? ''}');
    }
  }
}
