import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as math;

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:hub/actions/actions.dart';
import 'package:hub/app_state.dart';
import 'package:hub/backend/api_requests/api_calls.dart';
import 'package:hub/backend/api_requests/api_manager.dart';
import 'package:hub/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_widget.dart';
import 'package:hub/components/templates_components/message_notificaion_modal_template_component/message_notification_widget.dart';
import 'package:hub/components/templates_components/visit_request_template_component/visit_request_template_component_widget.dart';
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
import 'package:hub/flutter_flow/internationalization.dart';
import 'package:rxdart/rxdart.dart';

//

class PushNotificationService {
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
  final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();
  final Subject<RemoteMessage> _onMessage = BehaviorSubject<RemoteMessage>();
  final BehaviorSubject<BuildContext> _context =
      BehaviorSubject<BuildContext>();
  final BehaviorSubject<Map<String, dynamic>> _notificationDetails =
      BehaviorSubject<Map<String, dynamic>>();

  PushNotificationService() {
    _initializeLocalNotifications(_context);
    _createNotificationChannels();
  }

  Subject<RemoteMessage> getOnMessage() {
    return _onMessage;
  }

  Future<void> initialize(BuildContext context) async {
    _context.add(context);
    await _requestPermissions();
    _listenToForegroundMessages(context);
    _listenToBackgroundMessages();
    _listenToNotificationClicks(context);
    await updateDeviceToken();
  }

  Future<void> _requestPermissions() async {
    NotificationSettings settings = await _firebaseMessaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );
    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      log('User granted permission');
    } else {
      log('User declined or has not accepted permission');
    }
  }

  Map<String, dynamic> validJsonFromString(String? jsonString) {
    if (jsonString == null || jsonString.isEmpty) {
      return {};
    }

    // Passo 1 e 2: Adiciona aspas duplas em torno das chaves e valores que não estão corretamente delimitados
    String correctedJson = jsonString.replaceAllMapped(
        RegExp(r'([a-zA-Z0-9_]+)\s*:\s*([^",\}\]]+)'), (match) {
      var key = '"${match[1]!}"'; // Chaves sempre recebem aspas
      var value = match[2]!.trim();

      // Verifica se o valor é uma string (não numérica, booleana, nula ou objeto JSON)
      bool isStringValue = !RegExp(r'^-?\d+(\.\d+)?$').hasMatch(value) &&
          value != 'true' &&
          value != 'false' &&
          value != 'null' &&
          !value.startsWith('{') &&
          !value.endsWith('}');

      // Adiciona aspas duplas em torno do valor se for uma string
      String quotedValue = isStringValue ? '"$value"' : value;

      return '$key: $quotedValue';
    });

    // Passo 3: Tratar corretamente strings JSON aninhadas
    correctedJson =
        correctedJson.replaceAllMapped(RegExp(r'"{([^"]+)}"'), (match) {
      // Remove as aspas duplas extras em torno de objetos JSON aninhados
      return '{${match[1]!}}';
    });

    try {
      // Passo 4: Decodificar o JSON corrigido
      return jsonDecode(correctedJson);
    } catch (e) {
      log('Error decoding JSON: $e');
      return {};
    }
  }

  void _initializeLocalNotifications(
      BehaviorSubject<BuildContext> context) async {
    while (context.valueOrNull == null) {
      await Future.delayed(Duration(milliseconds: 100));
    }

    var initializationSettingsAndroid =
        AndroidInitializationSettings('mipmap/ic_fre_black');
    var initializationSettingsIOS = DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
    );
    var initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsIOS,
    );
    _flutterLocalNotificationsPlugin.initialize(
      initializationSettings,
      onDidReceiveNotificationResponse: (NotificationResponse response) async {
        log('Response payload:${response.payload}');
        if (response.payload != null) {
          try {
            Map<String, dynamic> message =
                validJsonFromString(response.payload!);
            log('Notification payload: $message');
            var data =
                _notificationDetails; // Assuming getOnMessage() now returns the latest RemoteMessage
            log('Extra: ${data.value}');
            _handleNotificationClick(message, extra: data.value);
          } catch (e) {
            log('Error decoding notification payload: $e');
          }
        }
      },
    );
  }

  void _createNotificationChannels() {
    List<String> actions = [
      'visit_request',
      'visit_response',
      'access',
      'mensagem',
      'enroll_cond',
      'miscellaneous'
    ];
    for (String action in actions) {
      _createNotificationChannel(action, "Channel for $action");
    }
  }

  void _createNotificationChannel(String channelId, String channelName) {
    _flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.createNotificationChannel(
          AndroidNotificationChannel(
            channelId, // Use o click_action como ID do canal
            channelName, // Nome descritivo baseado no click_action
            description: 'Channel for $channelName notifications',
            importance: Importance.max,
          ),
        );
  }

  void _listenToForegroundMessages(BuildContext context) {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      log('Got a message whilst in the foreground!');
      log('Message data: ${message.toMap()}');
      _onMessage.add(message);
      _notificationDetails.add(message.toMap()['notification']);
      _showNotification(message);
    });
  }

  void _listenToBackgroundMessages() {
    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  }

  void _listenToNotificationClicks(BuildContext context) {
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      log('Notification clicked!');

      _onMessage.add(message);
      log('Extra: ${message.notification?.body}');
      NotificationHandler().handleMessage(message.data, context);
    });
  }

  void configureTokenRefresh() {
    _firebaseMessaging.onTokenRefresh.listen(_handleTokenUpdate).onError((err) {
      log("Error refreshing token: $err");
    });
  }

  Future<void> _updateToken(String token) async {
    FFAppState().token = token;
    final ApiCallResponse? response = await _updateTokenOnServer(token);
    if (_isTokenUpdateSuccessful(response)) {
      log('Token updated successfully on server. Token: $token');
    } else {
      log('Error updating token on server');
    }
  }

  Future<void> _handleTokenUpdate(String newToken) async {
    log('Token refreshed: $newToken');
    await _updateToken(newToken);
  }

  Future<void> updateDeviceToken() async {
    configureTokenRefresh();

    final NotificationSettings settings =
        await _requestNotificationPermission();
    if (Platform.isIOS) await _fetchAndLogApnsToken(settings);

    final String? deviceToken = await _firebaseMessaging.getToken();
    if (deviceToken != null) {
      log('Push Messaging token: $deviceToken');
      await _updateToken(deviceToken);
    } else {
      log('Failed to get Firebase Messaging token');
    }
  }

  Future<NotificationSettings> _requestNotificationPermission() async {
    final NotificationSettings settings =
        await _firebaseMessaging.requestPermission();
    log(settings.authorizationStatus == AuthorizationStatus.authorized
        ? 'User granted permission'
        : 'User declined or has not accepted permission');
    return settings;
  }

  Future<void> _fetchAndLogApnsToken(NotificationSettings settings) async {
    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      final String? apnsToken = await _firebaseMessaging.getAPNSToken();
      log(apnsToken != null
          ? 'APNS Token: $apnsToken'
          : 'Failed to get APNS token');
    }
  }

  Future<ApiCallResponse?> _updateTokenOnServer(String deviceToken) async {
    return await PhpGroup.updToken.call(
      token: deviceToken,
      devid: FFAppState().devUUID,
      useruuid: FFAppState().userUUID,
    );
  }

  bool _isTokenUpdateSuccessful(ApiCallResponse? response) {
    return PhpGroup.updToken.error((response?.jsonBody ?? '')) == false;
  }

  String _getChannelIdBasedOnClickAction(String clickAction) {
    final baseId = clickAction.hashCode;
    return 'channel_$baseId';
  }

  void _showNotification(RemoteMessage message) async {
    String channelId =
        _getChannelIdBasedOnClickAction(message.data['click_action']);

    var androidDetails = AndroidNotificationDetails(
      channelId,
      'Channel Name for $channelId',
      channelDescription: 'Channel Description for $channelId',
      importance: Importance.max,
      priority: Priority.high,
    );
    var iOSDetails = DarwinNotificationDetails();
    var generalNotificationDetails =
        NotificationDetails(android: androidDetails, iOS: iOSDetails);

    log('Showing notification: ${message.messageId.hashCode}');
    await _flutterLocalNotificationsPlugin.show(
      // DateTime.now().millisecondsSinceEpoch % (1 << 31),
      math.Random().nextInt(1 << 30),
      message.notification?.title,
      message.notification?.body,
      generalNotificationDetails,
      payload: message.data.toString(),
    );
  }

  _handleNotificationClick(Map<String, dynamic> payload,
      {Map<String, dynamic> extra = const {}}) {
    switch (payload.isNotEmpty) {
      case true:
        // Print the 'data' property
        log('Notification payload: $payload');
        log('Extra: $extra');
        // Handle the message data as needed
        NotificationHandler().handleMessage(payload, _context.value,
            extra: extra.isEmpty ? {} : extra);
      // Access the 'data' property of 'RemoteMessage'
      case false:
        log('Notification payload is empty');
        // Handle the message notification as needed
        break;
    }
  }

  static Future<void> _firebaseMessagingBackgroundHandler(
      RemoteMessage message) async {
    log('Handling a background message: ${message.messageId}');
  }
}

class NotificationHandler {
  void handleMessage(Map<String, dynamic> message, BuildContext context,
      {Map<String, dynamic> extra = const {}}) {
    log('Notification Received!');
    message.forEach((key, value) {
      log('$key: $value');
    });

    switch (message['click_action']) {
      case 'visit_request':
        _showVisitRequestDialog(message, context);

        break;
      case '':
        break;
      case 'access':
        _showAcessNotificationModal(message, context);
        break;
      case 'mensagem':
        _showMessageNotificationDialog(message, context, extra);
        break;
      case 'enroll_cond':
        log('enroll_cond');
        break;
      default:
        log('Notification type not recognized');
    }
  }

  String _getIdBasedOnUserType(Map<String, dynamic> message) {
    if (message['USR_TIPO'].toString() == 'O') {
      // Retorna USR_ID se não estiver vazio/nulo, caso contrário retorna '0'
      return message['USR_ID'].toString().isEmpty
          ? '0'
          : message['USR_ID'].toString();
    } else {
      // Retorna USR_DOCUMENTO se não estiver vazio/nulo, caso contrário retorna '0'
      return message['USR_DOCUMENTO'].toString().isEmpty
          ? '0'
          : message['USR_DOCUMENTO'].toString();
    }
  }

  void _showAcessNotificationModal(
      Map<String, dynamic> message, BuildContext context) {
    log('Showing access notification dialog');
    log('USR_TIPO: ${message['USR_TIPO']}');
    log('USR_ID: ${message['USR_ID']}');
    log('USR_DOCUMENTO: ${message['USR_DOCUMENTO']}');
    showDialog(
      context: context,
      builder: (BuildContext context) {
        _getIdBasedOnUserType(message);
        return Dialog(
            backgroundColor: Colors.transparent,
            child: AccessNotificationModalTemplateComponentWidget(
              datetime: message['ACE_DATAHORA'].toString(),
              drive: message['ACI_DESCRICAO'].toString(),
              id: message['USR_TIPO'].toString() == 'O'
                  ? message['USR_ID'].toString() == ''
                      ? '0'
                      : message['USR_ID'].toString()
                  : message['USR_DOCUMENTO'].toString() == ''
                      ? '0'
                      : message['USR_DOCUMENTO'].toString(),
              name: message['PES_NOME'].toString(),
              type: message['USR_TIPO'],
            ));
      },
    );
  }

  void _showMessageNotificationDialog(Map<String, dynamic> message,
      BuildContext context, Map<String, dynamic> extra) {
    log('Showing message notification dialog');
    log('Notification "message": $message');
    showDialog(
      useSafeArea: true,
      context: context,
      builder: (BuildContext context) {
        return Dialog(
          backgroundColor: Colors.transparent,
          child: MessageNotificationModalTemplateComponentWidget(
            id: message['local']['CLI_ID'].toString(),
            from: message['remetente'].toString(),
            to: message['destinatario'].toString() == 'O'
                ? 'Morador'
                : 'Visitante',
            message: extra['body'].toString().isEmpty
                ? 'Unknown'
                : extra['body'].toString(),
          ),
        );
      },
    );
  }

  void _showVisitRequestDialog(
      Map<String, dynamic> message, BuildContext context) {
    log('Showing visit request notification dialog');
    showDialog(
      context: context,
      builder: (BuildContext context) {
        _getIdBasedOnUserType(message);
        return Dialog(
          backgroundColor: Colors.transparent,
          child: VisitRequestTemplateComponentWidget(
            vteName: message['mensagem'] ?? 'Unknown',
            vteReason: message['motivo'] ?? 'Unknown',
            vteMsg: message['mensagem'] ?? 'Unknown',
            vteDocument: message['documento'] ?? '',
            vteUUID: message['idVisitante'].toString(),
            vawRef: message['referencia'].toString(),
            vawStatus: 'S',
            labelsHashMap: Map<String, String>.from({
              'key': message['documento'],
              'value': 'E',
            }),
            imageHashMap: Map<String, String>.from({
              'key': message['documento'],
              'value': 'E',
            }),
            statusHashMap: [
              {
                FFLocalizations.of(context).getVariableText(
                  enText: 'Pending',
                  ptText: 'Pendente',
                ): FlutterFlowTheme.of(context).primary,
              },
            ],
            changeStatusAction: changeStatusAction,
          ),
        );
      },
    );
  }
}

class PushNotificationManager {
  final StreamController<RemoteMessage> _onMessageReceivedController =
      StreamController<RemoteMessage>.broadcast();

  Stream<RemoteMessage> get onMessageReceived =>
      _onMessageReceivedController.stream;

  PushNotificationManager() {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      _onMessageReceivedController.add(message);
    });
  }

  void dispose() {
    _onMessageReceivedController.close();
  }
}
