import 'dart:async';

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/widgets.dart';
import 'package:permission_handler/permission_handler.dart' as ph;
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'notif_permission.g.dart';

enum NotifPermStatus { notDetermined, granted, denied }

/// Wraps `firebase_messaging` + `permission_handler` for the Phase 4 Stage 4
/// notif gate. Reads/requests are platform-routed:
///   - iOS uses Firebase Messaging (which surfaces the system UNNotification
///     authorization status).
///   - Android 13+ uses `permission_handler` for `Permission.notification`
///     (POST_NOTIFICATIONS runtime). Older Android always reports granted.
class NotifPermission {
  const NotifPermission();

  Future<NotifPermStatus> readStatus() async {
    final phStatus = await ph.Permission.notification.status;
    return _mapPh(phStatus);
  }

  /// Shows the OS prompt only when status is [NotifPermStatus.notDetermined].
  /// Otherwise returns the current status without re-prompting (the OS would
  /// no-op anyway on a previously-resolved permission).
  Future<NotifPermStatus> request() async {
    final current = await readStatus();
    if (current != NotifPermStatus.notDetermined) return current;

    // Firebase Messaging requestPermission triggers the iOS prompt; on Android
    // it is a no-op for permission UI but registers the FCM iOS APNS token.
    // We still call permission_handler.request() so Android 13+ shows the
    // POST_NOTIFICATIONS dialog.
    await FirebaseMessaging.instance.requestPermission();
    final phResult = await ph.Permission.notification.request();
    return _mapPh(phResult);
  }

  Future<void> openAppSettings() => ph.openAppSettings();

  NotifPermStatus _mapPh(ph.PermissionStatus s) {
    if (s.isGranted || s.isLimited || s.isProvisional) {
      return NotifPermStatus.granted;
    }
    if (s.isDenied) return NotifPermStatus.notDetermined;
    // permanentlyDenied + restricted both behave like "denied — open settings".
    return NotifPermStatus.denied;
  }
}

final _helperProvider = Provider<NotifPermission>((_) => const NotifPermission());

/// Cached notif permission status. Auto-refreshes on app foreground via an
/// internal `WidgetsBindingObserver` — there is no shared `appLifecycleProvider`
/// in this codebase yet, so the observer is owned here.
@Riverpod(keepAlive: true)
class NotifPermissionStatus extends _$NotifPermissionStatus {
  _LifecycleHook? _hook;

  @override
  Future<NotifPermStatus> build() async {
    _hook ??= _LifecycleHook(_onResumed);
    ref.onDispose(() {
      _hook?.detach();
      _hook = null;
    });
    return ref.read(_helperProvider).readStatus();
  }

  /// Triggers the OS prompt (only if [NotifPermStatus.notDetermined]) and
  /// re-publishes the resolved status.
  Future<NotifPermStatus> request() async {
    final result = await ref.read(_helperProvider).request();
    state = AsyncData(result);
    return result;
  }

  Future<void> openAppSettings() =>
      ref.read(_helperProvider).openAppSettings();

  /// Force a re-read — used after returning from app settings.
  Future<void> refresh() async {
    final s = await ref.read(_helperProvider).readStatus();
    if (state.valueOrNull == s) return;
    state = AsyncData(s);
  }

  void _onResumed() {
    // Fire-and-forget; refresh is idempotent.
    // ignore: unawaited_futures
    refresh();
  }
}

class _LifecycleHook with WidgetsBindingObserver {
  _LifecycleHook(this._onResumed) {
    WidgetsBinding.instance.addObserver(this);
  }

  final VoidCallback _onResumed;

  void detach() {
    WidgetsBinding.instance.removeObserver(this);
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _onResumed();
    }
  }
}
