import 'dart:convert';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:travel_mate/services/logger_service.dart';
import 'package:flutter/material.dart';
import 'package:travel_mate/services/error_service.dart';
import 'package:travel_mate/repositories/group_repository.dart';
import 'package:travel_mate/repositories/account_repository.dart';
import 'package:travel_mate/components/group/chat_group_content.dart';
import 'package:travel_mate/components/account/group_expenses_page.dart';

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  LoggerService.info('Handling a background message: ${message.messageId}');
}

class NotificationService {
  static final NotificationService _instance = NotificationService._internal();
  factory NotificationService() => _instance;
  NotificationService._internal();

  late final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
  late final FlutterLocalNotificationsPlugin _localNotifications =
      FlutterLocalNotificationsPlugin();

  bool _isInitialized = false;

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

    // Request permissions
    await _requestPermissions();

    // Initialize local notifications
    const androidSettings = AndroidInitializationSettings(
      '@mipmap/ic_launcher',
    );
    const iosSettings = DarwinInitializationSettings();
    const initSettings = InitializationSettings(
      android: androidSettings,
      iOS: iosSettings,
    );

    await _localNotifications.initialize(
      initSettings,
      onDidReceiveNotificationResponse: (details) {
        // Handle notification tap
        LoggerService.info('Notification tapped: ${details.payload}');
        if (details.payload != null) {
          try {
            final data = json.decode(details.payload!) as Map<String, dynamic>;
            _handleNotificationTap(data);
          } catch (e) {
            LoggerService.error('Error parsing notification payload', error: e);
          }
        }
      },
    );

    // Handle foreground messages
    FirebaseMessaging.onMessage.listen(_handleForegroundMessage);

    // Handle token refresh
    FirebaseMessaging.instance.onTokenRefresh.listen(_onTokenRefresh);

    // Setup interacted message (Deep Linking)
    // We don't call this here anymore, it will be called from HomePage
    // await setupInteractedMessage();

    _isInitialized = true;
    LoggerService.info('NotificationService initialized');

    // Print current token for debugging
    final token = await getFCMToken();
    LoggerService.info('Current FCM Token: $token');
  }

  /// Sets up the background message listener.
  /// Should be called when the app is ready to handle navigation.
  void startListening() {
    // Handle any interaction when the app is in the background via a
    // Stream listener
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      _handleNotificationTap(message.data);
    });
  }

  /// Checks for an initial message (app opened from terminated state)
  /// and handles it if present.
  Future<void> handleInitialMessage() async {
    // Get any messages which caused the application to open from
    // a terminated state.
    RemoteMessage? initialMessage = await _firebaseMessaging
        .getInitialMessage();

    if (initialMessage != null) {
      LoggerService.info('Found initial message: ${initialMessage.data}');
      _handleNotificationTap(initialMessage.data);
    }
  }

  Future<void> _handleNotificationTap(Map<String, dynamic> data) async {
    LoggerService.info('Handling notification tap with data: $data');
    // DEBUG: Show snackbar to verify payload
    // ErrorService().showSnackbar(message: 'Debug: Payload $data', isError: false);

    final type = data['type'];

    try {
      if (type == 'message') {
        final groupId = data['groupId'];
        if (groupId != null) {
          final groupRepository = GroupRepository();
          final group = await groupRepository.getGroupById(groupId);
          if (group != null) {
            ErrorService.navigatorKey.currentState?.push(
              MaterialPageRoute(
                builder: (context) => ChatGroupContent(group: group),
              ),
            );
          } else {
            LoggerService.error('Group not found: $groupId');
            // ErrorService().showError(message: 'Groupe introuvable: $groupId');
          }
        } else {
          LoggerService.error('Missing groupId in payload');
          // ErrorService().showError(message: 'Payload invalide: groupId manquant');
        }
      } else if (type == 'expense') {
        final tripId = data['tripId'];
        if (tripId != null) {
          final accountRepository = AccountRepository();
          final groupRepository = GroupRepository();

          final account = await accountRepository.getAccountByTripId(tripId);
          final group = await groupRepository.getGroupByTripId(tripId);

          if (account != null && group != null) {
            ErrorService.navigatorKey.currentState?.push(
              MaterialPageRoute(
                builder: (context) =>
                    GroupExpensesPage(account: account, group: group),
              ),
            );
          } else {
            LoggerService.error('Account or Group not found for trip: $tripId');
            // ErrorService().showError(message: 'Compte ou Groupe introuvable');
          }
        }
      } else {
        LoggerService.info('Unknown notification type: $type');
      }
    } catch (e) {
      LoggerService.error('Error handling notification tap: $e');
      ErrorService().showError(message: 'Erreur navigation: $e');
    }
  }

  Future<void> _onTokenRefresh(String newToken) async {
    LoggerService.info('FCM Token refreshed: $newToken');
    // We need the user ID to save the token.
    // Since this service is a singleton, we might not have direct access to the user ID here
    // without injecting the repository or bloc.
    // For now, we rely on the AuthBloc to update the token on login/start.
    // Ideally, we should save it here if we have the user ID.
  }

  Future<void> _requestPermissions() async {
    NotificationSettings settings = await _firebaseMessaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    LoggerService.info(
      'User granted permission: ${settings.authorizationStatus}',
    );
  }

  Future<String?> getFCMToken() async {
    try {
      if (Platform.isIOS) {
        String? apnsToken = await _firebaseMessaging.getAPNSToken();
        int retries = 0;
        while (apnsToken == null && retries < 10) {
          LoggerService.info(
            'Waiting for APNS token... (Attempt ${retries + 1}/10)',
          );
          await Future.delayed(const Duration(seconds: 2));
          apnsToken = await _firebaseMessaging.getAPNSToken();
          retries++;
        }
        if (apnsToken == null) {
          LoggerService.error('APNS token not available after retries');
          return null;
        }
      }

      final token = await _firebaseMessaging.getToken();
      LoggerService.info('NotificationService - FCM Token: $token');
      return token;
    } catch (e) {
      LoggerService.error('Error getting FCM token: $e');
      return null;
    }
  }

  Future<void> saveTokenToFirestore(String userId) async {
    try {
      final token = await getFCMToken();
      if (token != null) {
        await FirebaseFirestore.instance.collection('users').doc(userId).set({
          'fcmToken': token,
        }, SetOptions(merge: true));
        LoggerService.info('FCM Token saved to Firestore for user: $userId');
      }
    } catch (e) {
      LoggerService.error('Error saving FCM token to Firestore: $e');
    }
  }

  Future<void> _handleForegroundMessage(RemoteMessage message) async {
    LoggerService.info('Got a message whilst in the foreground!');
    LoggerService.info('Message data: ${message.data}');

    if (message.notification != null) {
      LoggerService.info(
        'Message also contained a notification: ${message.notification}',
      );

      // Show local notification
      const androidDetails = AndroidNotificationDetails(
        'high_importance_channel',
        'High Importance Notifications',
        importance: Importance.max,
        priority: Priority.high,
      );
      const iosDetails = DarwinNotificationDetails();
      const details = NotificationDetails(
        android: androidDetails,
        iOS: iosDetails,
      );

      await _localNotifications.show(
        message.hashCode,
        message.notification?.title,
        message.notification?.body,
        details,
      );
    }
  }
}
