import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'package:just_audio/just_audio.dart' as JustAudio;
import 'package:just_audio/just_audio.dart';
import 'package:mohem_flutter_app/api/chat/chat_api_client.dart';
import 'package:mohem_flutter_app/api/my_team/my_team_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/encryption.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart';
import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart';
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as userLoginToken;
import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart';
import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:signalr_netcore/hub_connection.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:uuid/uuid.dart';
import 'package:flutter/material.dart' as Material;

class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
  ScrollController scrollController = ScrollController();

  TextEditingController message = TextEditingController();
  TextEditingController search = TextEditingController();
  List<SingleUserChatModel> userChatHistory = [], repliedMsg = [];
  List<ChatUser>? pChatHistory, searchedChats;
  String chatCID = '';
  bool isLoading = true;
  bool isChatScreenActive = false;
  int receiverID = 0;
  late File selectedFile;
  String sFileType = "";

  List<ChatUser> favUsersList = [];
  int paginationVal = 0;
  int? cTypingUserId = 0;
  bool isTextMsg = false, isReplyMsg = false, isAttachmentMsg = false, isVoiceMsg = false;

  // Audio Recoding Work
  Timer? _timer;
  int _recodeDuration = 0;
  bool isRecoding = false;
  bool isPause = false;
  bool isPlaying = false;
  String? path;
  String? musicFile;
  late Directory appDirectory;
  late RecorderController recorderController;
  late PlayerController playerController;
  List<GetEmployeeSubordinatesList> getEmployeeSubordinatesList = [];
  List<ChatUser> teamMembersList = [];

  Material.TextDirection textDirection = Material.TextDirection.ltr;

  //Chat Home Page Counter
  int chatUConvCounter = 0;

  /// Search Provider
  List<ChatUser>? chatUsersList = [];
  int pageNo = 1;

  Future<void> getUserAutoLoginToken() async {
    userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
    if (userLoginResponse.response != null) {
      AppState().setchatUserDetails = userLoginResponse;
    } else {
      AppState().setchatUserDetails = userLoginResponse;
      Utils.showToast(
        userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr",
      );
    }
  }

  Future<void> buildHubConnection() async {
    chatHubConnection = await getHubConnection();
    await chatHubConnection.start();
    if (kDebugMode) {
      logger.i("Hub Conn: Startedddddddd");
    }
    chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
    chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion);
  }

  Future<HubConnection> getHubConnection() async {
    HubConnection hub;
    HttpConnectionOptions httpOp = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
    hub = HubConnectionBuilder()
        .withUrl(ApiConsts.chatHubConnectionUrl + "?UserId=${AppState().chatDetails!.response!.id}&source=Desktop&access_token=${AppState().chatDetails!.response!.token}", options: httpOp)
        .withAutomaticReconnect(retryDelays: <int>[2000, 5000, 10000, 20000]).build();
    return hub;
  }

  void registerEvents() {
    chatHubConnection.on("OnUpdateUserStatusAsync", changeStatus);
    //   chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
    chatHubConnection.on("OnSubmitChatAsync", OnSubmitChatAsync);
    chatHubConnection.on("OnUserTypingAsync", onUserTyping);
    chatHubConnection.on("OnUserCountAsync", userCountAsync);
    //  chatHubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow);
    chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered);
    chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus);
    if (kDebugMode) {
      logger.i("All listeners registered");
    }
  }

  Future<void> getUserRecentChats() async {
    ChatUserModel recentChat = await ChatApiClient().getRecentChats();
    ChatUserModel favUList = await ChatApiClient().getFavUsers();
    if (favUList.response != null && recentChat.response != null) {
      favUsersList = favUList.response!;
      favUsersList.sort((ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(b.userName!.toLowerCase()));
      for (dynamic user in recentChat.response!) {
        for (dynamic favUser in favUList.response!) {
          if (user.id == favUser.id) {
            user.isFav = favUser.isFav;
          }
        }
      }
    }
    pChatHistory = recentChat.response ?? [];
    pChatHistory!.sort((ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(b.userName!.toLowerCase()));
    searchedChats = pChatHistory;
    isLoading = false;
    await invokeUserChatHistoryNotDeliveredAsync(userId: int.parse(AppState().chatDetails!.response!.id.toString()));
    sort();
    notifyListeners();
    if (searchedChats!.isNotEmpty || favUsersList.isNotEmpty) {
      getUserImages();
    }
  }

  Future invokeUserChatHistoryNotDeliveredAsync({required int userId}) async {
    await chatHubConnection.invoke("GetUserChatHistoryNotDeliveredAsync", args: [userId]);
    return "";
  }

  void getSingleUserChatHistory({required int senderUID, required int receiverUID, required bool loadMore, bool isNewChat = false}) async {
    isLoading = true;
    if (isNewChat) userChatHistory = [];
    if (!loadMore) paginationVal = 0;
    isChatScreenActive = true;
    receiverID = receiverUID;
    Response response = await ChatApiClient().getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal);
    if (response.statusCode == 204) {
      if (isNewChat) {
        userChatHistory = [];
      } else if (loadMore) {}
    } else {
      if (loadMore) {
        List<SingleUserChatModel> temp = getSingleUserChatModel(response.body).reversed.toList();
        userChatHistory.addAll(temp);
      } else {
        userChatHistory = getSingleUserChatModel(response.body).reversed.toList();
      }
    }
    isLoading = false;
    notifyListeners();

    if (isChatScreenActive && receiverUID == receiverID) {
      markRead(userChatHistory, receiverUID);
    }

    generateConvId();
  }

  void generateConvId() async {
    Uuid uuid = const Uuid();
    chatCID = uuid.v4();
  }

  void markRead(List<SingleUserChatModel> data, int receiverID) {
    for (SingleUserChatModel element in data!) {
      if (AppState().chatDetails!.response!.id! == element.targetUserId) {
        if (element.isSeen != null) {
          if (!element.isSeen!) {
            element.isSeen = true;
            dynamic data = [
              {
                "userChatHistoryId": element.userChatHistoryId,
                "TargetUserId": element.currentUserId == receiverID ? element.currentUserId : element.targetUserId,
                "isDelivered": true,
                "isSeen": true,
              }
            ];
            updateUserChatHistoryStatusAsync(data);
            notifyListeners();
          }
        }
        for (ChatUser element in searchedChats!) {
          if (element.id == receiverID) {
            element.unreadMessageCount = 0;
            chatUConvCounter = 0;
          }
        }
      }
    }
    notifyListeners();
  }

  void updateUserChatHistoryStatusAsync(List data) {
    try {
      chatHubConnection.invoke("UpdateUserChatHistoryStatusAsync", args: [data]);
    } catch (e) {
      throw e;
    }
  }

  void updateUserChatHistoryOnMsg(List data) {
    try {
      chatHubConnection.invoke("UpdateUserChatHistoryStatusAsync", args: [data]);
    } catch (e) {
      throw e;
    }
  }

  List<SingleUserChatModel> getSingleUserChatModel(String str) => List<SingleUserChatModel>.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x)));

  Future<dynamic> uploadAttachments(String userId, File file) async {
    dynamic result;
    try {
      Object? response = await ChatApiClient().uploadMedia(userId, file);
      if (response != null) {
        result = response;
      } else {
        result = [];
      }
    } catch (e) {
      throw e;
    }
    return result;
  }

  void updateUserChatStatus(List<Object?>? args) {
    dynamic items = args!.toList();
    for (var cItem in items[0]) {
      for (SingleUserChatModel chat in userChatHistory) {
        if (cItem["contantNo"].toString() == chat.contantNo.toString()) {
          chat.isSeen = cItem["isSeen"];
          chat.isDelivered = cItem["isDelivered"];
        }
      }
    }
    notifyListeners();
  }

  void onChatSeen(List<Object?>? args) {
    dynamic items = args!.toList();
    // for (var user in searchedChats!) {
    //   if (user.id == items.first["id"]) {
    //     user.userStatus = items.first["userStatus"];
    //   }
    // }
    // notifyListeners();
  }

  void userCountAsync(List<Object?>? args) {
    dynamic items = args!.toList();
    // logger.d(items);
    //logger.d("---------------------------------User Count Async -------------------------------------");
    //logger.d(items);
    // for (var user in searchedChats!) {
    //   if (user.id == items.first["id"]) {
    //     user.userStatus = items.first["userStatus"];
    //   }
    // }
    // notifyListeners();
  }

  void updateChatHistoryWindow(List<Object?>? args) {
    dynamic items = args!.toList();
    if (kDebugMode) {
      logger.i("---------------------------------Update Chat History Windows  Async -------------------------------------");
    }
    logger.d(items);
    // for (var user in searchedChats!) {
    //   if (user.id == items.first["id"]) {
    //     user.userStatus = items.first["userStatus"];
    //   }
    // }
    // notifyListeners();
  }

  void chatNotDelivered(List<Object?>? args) {
    dynamic items = args!.toList();
    for (dynamic item in items[0]) {
      for (ChatUser element in searchedChats!) {
        if (element.id == item["currentUserId"]) {
          int? val = element.unreadMessageCount ?? 0;
          element.unreadMessageCount = val! + 1;
        }
      }
    }
    notifyListeners();
  }

  void changeStatus(List<Object?>? args) {
    dynamic items = args!.toList();
    for (ChatUser user in searchedChats!) {
      if (user.id == items.first["id"]) {
        user.userStatus = items.first["userStatus"];
      }
    }
    if (teamMembersList.isNotEmpty) {
      for (ChatUser user in teamMembersList!) {
        if (user.id == items.first["id"]) {
          user.userStatus = items.first["userStatus"];
        }
      }
    }

    notifyListeners();
  }

  void filter(String value) async {
    List<ChatUser>? tmp = [];
    if (value.isEmpty || value == "") {
      tmp = pChatHistory;
    } else {
      for (ChatUser element in pChatHistory!) {
        if (element.userName!.toLowerCase().contains(value.toLowerCase())) {
          tmp.add(element);
        }
      }
    }
    searchedChats = tmp;
    notifyListeners();
  }

  Future<void> onMsgReceived(List<Object?>? parameters) async {
    List<SingleUserChatModel> data = [], temp = [];
    for (dynamic msg in parameters!) {
      data = getSingleUserChatModel(jsonEncode(msg));
      temp = getSingleUserChatModel(jsonEncode(msg));
      data.first.targetUserId = temp.first.currentUserId;
      data.first.targetUserName = temp.first.currentUserName;
      data.first.targetUserEmail = temp.first.currentUserEmail;
      data.first.currentUserId = temp.first.targetUserId;
      data.first.currentUserName = temp.first.targetUserName;
      data.first.currentUserEmail = temp.first.targetUserEmail;

      if (data.first.fileTypeId == 12 || data.first.fileTypeId == 4 || data.first.fileTypeId == 3) {
        data.first.image = await ChatApiClient().downloadURL(fileName: data.first.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg");
      }
      if (data.first.userChatReplyResponse != null) {
        if (data.first.fileTypeResponse != null) {
          if (data.first.userChatReplyResponse!.fileTypeId == 12 || data.first.userChatReplyResponse!.fileTypeId == 4 || data.first.userChatReplyResponse!.fileTypeId == 3) {
            data.first.userChatReplyResponse!.image =
                await ChatApiClient().downloadURL(fileName: data.first.userChatReplyResponse!.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg");
            data.first.userChatReplyResponse!.isImageLoaded = true;
          }
        }
      }
    }

    if (searchedChats != null) {
      dynamic contain = searchedChats!.where((ChatUser element) => element.id == data.first.currentUserId);
      if (contain.isEmpty) {
        List<String> emails = [];
        emails.add(await EmailImageEncryption().encrypt(val: data.first.currentUserEmail!));
        List<ChatUserImageModel> chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails);
        searchedChats!.add(
          ChatUser(
            id: data.first.currentUserId,
            userName: data.first.currentUserName,
            email: data.first.currentUserEmail,
            unreadMessageCount: 0,
            isImageLoading: false,
            image: chatImages!.first.profilePicture ?? "",
            isImageLoaded: true,
            userStatus: 1,
            isTyping: false,
            userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, data.first.currentUserId.toString()),
          ),
        );
      }
    }
    setMsgTune();
    if (isChatScreenActive && data.first.currentUserId == receiverID) {
      userChatHistory.insert(0, data.first);
    } else {
      if (searchedChats != null) {
        for (ChatUser user in searchedChats!) {
          if (user.id == data.first.currentUserId) {
            int tempCount = user.unreadMessageCount ?? 0;
            user.unreadMessageCount = tempCount + 1;
          }
        }
        sort();
      }
    }

    List<Object> list = [
      {
        "userChatHistoryId": data.first.userChatHistoryId,
        "TargetUserId": temp.first.targetUserId,
        "isDelivered": true,
        "isSeen": isChatScreenActive && data.first.currentUserId == receiverID ? true : false
      }
    ];
    updateUserChatHistoryOnMsg(list);
    invokeChatCounter(userId: AppState().chatDetails!.response!.id!);
    notifyListeners();
  }

  void OnSubmitChatAsync(List<Object?>? parameters) {
    print(isChatScreenActive);
    print(receiverID);
    print(isChatScreenActive);
    logger.i(parameters);
    List<SingleUserChatModel> data = [], temp = [];
    for (dynamic msg in parameters!) {
      data = getSingleUserChatModel(jsonEncode(msg));
      temp = getSingleUserChatModel(jsonEncode(msg));
      data.first.targetUserId = temp.first.currentUserId;
      data.first.targetUserName = temp.first.currentUserName;
      data.first.targetUserEmail = temp.first.currentUserEmail;
      data.first.currentUserId = temp.first.targetUserId;
      data.first.currentUserName = temp.first.targetUserName;
      data.first.currentUserEmail = temp.first.targetUserEmail;
    }
    if (isChatScreenActive && data.first.currentUserId == receiverID) {
      int index = userChatHistory.indexWhere((SingleUserChatModel element) => element.userChatHistoryId == 0);
      logger.d(index);
      userChatHistory[index] = data.first;
    }

    notifyListeners();
  }

  void sort() {
    searchedChats!.sort(
      (ChatUser a, ChatUser b) => b.unreadMessageCount!.compareTo(a.unreadMessageCount!),
    );
  }

  void onUserTyping(List<Object?>? parameters) {
    for (ChatUser user in searchedChats!) {
      if (user.id == parameters![1] && parameters[0] == true) {
        user.isTyping = parameters[0] as bool?;
        Future.delayed(
          const Duration(seconds: 2),
          () {
            user.isTyping = false;
            notifyListeners();
          },
        );
      }
    }
    notifyListeners();
  }

  int getFileType(String value) {
    switch (value) {
      case ".pdf":
        return 1;
      case ".png":
        return 3;
      case ".txt":
        return 5;
      case ".jpg":
        return 12;
      case ".jpeg":
        return 4;
      case ".xls":
        return 7;
      case ".xlsx":
        return 7;
      case ".doc":
        return 6;
      case ".docx":
        return 6;
      case ".ppt":
        return 8;
      case ".pptx":
        return 8;
      case ".zip":
        return 2;
      case ".rar":
        return 2;
      case ".aac":
        return 13;
      case ".mp3":
        return 14;
      default:
        return 0;
    }
  }

  String getFileTypeDescription(String value) {
    switch (value) {
      case ".pdf":
        return "application/pdf";
      case ".png":
        return "image/png";
      case ".txt":
        return "text/plain";
      case ".jpg":
        return "image/jpg";
      case ".jpeg":
        return "image/jpeg";
      case ".ppt":
        return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
      case ".pptx":
        return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
      case ".doc":
        return "application/vnd.openxmlformats-officedocument.wordprocessingm";
      case ".docx":
        return "application/vnd.openxmlformats-officedocument.wordprocessingm";
      case ".xls":
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
      case ".xlsx":
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
      case ".zip":
        return "application/octet-stream";
      case ".rar":
        return "application/octet-stream";
      case ".aac":
        return "audio/aac";
      case ".mp3":
        return "audio/mp3";
      default:
        return "";
    }
  }

  Future<void> sendChatToServer(
      {required int chatEventId,
      required fileTypeId,
      required int targetUserId,
      required String targetUserName,
      required chatReplyId,
      required bool isAttachment,
      required bool isReply,
      Uint8List? image,
      required bool isImageLoaded,
      String? userEmail,
      int? userStatus,
      File? voiceFile,
      required bool isVoiceAttached}) async {
    Uuid uuid = const Uuid();
    String contentNo = uuid.v4();
    String msg;
    if (isVoiceAttached) {
      msg = voiceFile!.path.split("/").last;
    } else {
      msg = message.text;
      logger.w(msg);
    }
    SingleUserChatModel data = SingleUserChatModel(
        userChatHistoryId: 0,
        chatEventId: chatEventId,
        chatSource: 1,
        contant: msg,
        contantNo: contentNo,
        conversationId: chatCID,
        createdDate: DateTime.now(),
        currentUserId: AppState().chatDetails!.response!.id,
        currentUserName: AppState().chatDetails!.response!.userName,
        targetUserId: targetUserId,
        targetUserName: targetUserName,
        isReplied: false,
        fileTypeId: fileTypeId,
        userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null,
        fileTypeResponse: isAttachment
            ? FileTypeResponse(
                fileTypeId: fileTypeId,
                fileTypeName: isVoiceMsg ? getFileExtension(voiceFile!.path).toString() : getFileExtension(selectedFile.path).toString(),
                fileKind: "file",
                fileName: isVoiceMsg ? msg : selectedFile.path.split("/").last,
                fileTypeDescription: isVoiceMsg ? getFileTypeDescription(getFileExtension(voiceFile!.path).toString()) : getFileTypeDescription(getFileExtension(selectedFile.path).toString()),
              )
            : null,
        image: image,
        isImageLoaded: isImageLoaded,
        voice: isVoiceMsg ? voiceFile! : null,
        voiceController: isVoiceMsg ? AudioPlayer() : null);
    if (kDebugMode) {
      logger.i("model data: " + jsonEncode(data));
    }
    userChatHistory.insert(0, data);
    isTextMsg = false;
    isReplyMsg = false;
    isAttachmentMsg = false;
    isVoiceMsg = false;
    sFileType = "";
    message.clear();
    notifyListeners();

    String chatData =
        '{"contant":"$msg","contantNo":"$contentNo","chatEventId":$chatEventId,"fileTypeId": $fileTypeId,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"chatReplyId":$chatReplyId,"conversationId":"$chatCID"}';
    await chatHubConnection.invoke("AddChatUserAsync", args: <Object>[json.decode(chatData)]);
  }

  void sendChatMessage(BuildContext context, {required int targetUserId, required int userStatus, required String userEmail, required String targetUserName}) async {
    if (kDebugMode) {
      print("====================== Values ============================");
      print("Is Text " + isTextMsg.toString());
      print("isReply " + isReplyMsg.toString());
      print("isAttachment " + isAttachmentMsg.toString());
      print("isVoice " + isVoiceMsg.toString());
    }
    //Text
    if (isTextMsg && !isAttachmentMsg && !isVoiceMsg && !isReplyMsg) {
      logger.d("// Normal Text Message");
      if (message.text.isEmpty) {
        return;
      }
      sendChatToServer(
          chatEventId: 1,
          fileTypeId: null,
          targetUserId: targetUserId,
          targetUserName: targetUserName,
          isAttachment: false,
          chatReplyId: null,
          isReply: false,
          isImageLoaded: false,
          image: null,
          isVoiceAttached: false,
          userEmail: userEmail,
          userStatus: userStatus);
    } else if (isTextMsg && !isAttachmentMsg && !isVoiceMsg && isReplyMsg) {
      logger.d("// Text Message as Reply");
      if (message.text.isEmpty) {
        return;
      }
      sendChatToServer(
          chatEventId: 1,
          fileTypeId: null,
          targetUserId: targetUserId,
          targetUserName: targetUserName,
          chatReplyId: repliedMsg.first.userChatHistoryId,
          isAttachment: false,
          isReply: true,
          isImageLoaded: repliedMsg.first.isImageLoaded!,
          image: repliedMsg.first.image,
          isVoiceAttached: false,
          voiceFile: null,
          userEmail: userEmail,
          userStatus: userStatus);
    }
    //  Attachment
    else if (!isTextMsg && isAttachmentMsg && !isVoiceMsg && !isReplyMsg) {
      logger.d("// Normal Image Message");
      Utils.showLoading(context);
      dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile);
      String? ext = getFileExtension(selectedFile.path);
      Utils.hideLoading(context);
      sendChatToServer(
          chatEventId: 2,
          fileTypeId: getFileType(ext.toString()),
          targetUserId: targetUserId,
          targetUserName: targetUserName,
          isAttachment: true,
          chatReplyId: null,
          isReply: false,
          isImageLoaded: true,
          image: selectedFile.readAsBytesSync(),
          isVoiceAttached: false,
          userEmail: userEmail,
          userStatus: userStatus);
    } else if (!isTextMsg && isAttachmentMsg && !isVoiceMsg && isReplyMsg) {
      logger.d("// Image as Reply Msg");
      Utils.showLoading(context);
      dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile);
      String? ext = getFileExtension(selectedFile.path);
      Utils.hideLoading(context);
      sendChatToServer(
          chatEventId: 2,
          fileTypeId: getFileType(ext.toString()),
          targetUserId: targetUserId,
          targetUserName: targetUserName,
          isAttachment: true,
          chatReplyId: repliedMsg.first.userChatHistoryId,
          isReply: true,
          isImageLoaded: true,
          image: selectedFile.readAsBytesSync(),
          isVoiceAttached: false,
          userEmail: userEmail,
          userStatus: userStatus);
    }
    //Voice

    else if (!isTextMsg && !isAttachmentMsg && isVoiceMsg && !isReplyMsg) {
      logger.d("// Normal Voice Message");

      if (!isPause) {
        path = await recorderController.stop(false);
      }
      if (kDebugMode) {
        logger.i("path:" + path!);
      }
      File voiceFile = File(path!);
      voiceFile.readAsBytesSync();
      _timer?.cancel();
      isPause = false;
      isPlaying = false;
      isRecoding = false;
      Utils.showLoading(context);
      dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), voiceFile);
      String? ext = getFileExtension(voiceFile.path);
      Utils.hideLoading(context);
      sendChatToServer(
          chatEventId: 2,
          fileTypeId: getFileType(ext.toString()),
          targetUserId: targetUserId,
          targetUserName: targetUserName,
          chatReplyId: null,
          isAttachment: true,
          isReply: isReplyMsg,
          isImageLoaded: false,
          voiceFile: voiceFile,
          isVoiceAttached: true,
          userEmail: userEmail,
          userStatus: userStatus);
      notifyListeners();
    } else if (!isTextMsg && !isAttachmentMsg && isVoiceMsg && isReplyMsg) {
      logger.d("// Voice as Reply Msg");

      if (!isPause) {
        path = await recorderController.stop(false);
      }
      if (kDebugMode) {
        logger.i("path:" + path!);
      }
      File voiceFile = File(path!);
      voiceFile.readAsBytesSync();
      _timer?.cancel();
      isPause = false;
      isPlaying = false;
      isRecoding = false;

      Utils.showLoading(context);
      dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), voiceFile);
      String? ext = getFileExtension(voiceFile.path);
      Utils.hideLoading(context);
      sendChatToServer(
          chatEventId: 2,
          fileTypeId: getFileType(ext.toString()),
          targetUserId: targetUserId,
          targetUserName: targetUserName,
          chatReplyId: null,
          isAttachment: true,
          isReply: isReplyMsg,
          isImageLoaded: false,
          voiceFile: voiceFile,
          isVoiceAttached: true,
          userEmail: userEmail,
          userStatus: userStatus);
      notifyListeners();
    }
    if (searchedChats != null) {
      dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
      if (contain.isEmpty) {
        List<String> emails = [];
        emails.add(await EmailImageEncryption().encrypt(val: userEmail));
        List<ChatUserImageModel> chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails);
        searchedChats!.add(
          ChatUser(
            id: targetUserId,
            userName: targetUserName,
            unreadMessageCount: 0,
            email: userEmail,
            isImageLoading: false,
            image: chatImages.first.profilePicture ?? "",
            isImageLoaded: true,
            isTyping: false,
            isFav: false,
            userStatus: userStatus,
            userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, targetUserId.toString()),
          ),
        );
        notifyListeners();
      }
    }
    // else {
    //   List<String> emails = [];
    //   emails.add(await EmailImageEncryption().encrypt(val: userEmail));
    //   List<ChatUserImageModel> chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails);
    //   searchedChats!.add(
    //     ChatUser(
    //       id: targetUserId,
    //       userName: targetUserName,
    //       unreadMessageCount: 0,
    //       email: userEmail,
    //       isImageLoading: false,
    //       image: chatImages.first.profilePicture ?? "",
    //       isImageLoaded: true,
    //       isTyping: false,
    //       isFav: false,
    //       userStatus: userStatus,
    //       userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, targetUserId.toString()),
    //     ),
    //   );
    //   notifyListeners();
    // }
  }

  void selectImageToUpload(BuildContext context) {
    ImageOptions.showImageOptionsNew(context, true, (String image, File file) async {
      if (checkFileSize(file.path)) {
        selectedFile = file;
        isAttachmentMsg = true;
        isTextMsg = false;
        sFileType = getFileExtension(file.path)!;
        message.text = file.path.split("/").last;
        Navigator.of(context).pop();
      } else {
        Utils.showToast("Max 1 mb size is allowed to upload");
      }
      notifyListeners();
    });
  }

  void removeAttachment() {
    isAttachmentMsg = false;
    sFileType = "";
    message.text = '';
    notifyListeners();
  }

  String? getFileExtension(String fileName) {
    try {
      if (kDebugMode) {
        logger.i("ext: " + "." + fileName.split('.').last);
      }
      return "." + fileName.split('.').last;
    } catch (e) {
      return null;
    }
  }

  bool checkFileSize(String path) {
    int fileSizeLimit = 1024;
    File f = File(path);
    double fileSizeInKB = f.lengthSync() / 1024;
    double fileSizeInMB = fileSizeInKB / 1024;
    if (fileSizeInKB > fileSizeLimit) {
      return false;
    } else {
      return true;
    }
  }

  String getType(String type) {
    switch (type) {
      case ".pdf":
        return "assets/images/pdf.svg";
      case ".png":
        return "assets/images/png.svg";
      case ".txt":
        return "assets/icons/chat/txt.svg";
      case ".jpg":
        return "assets/images/jpg.svg";
      case ".jpeg":
        return "assets/images/jpg.svg";
      case ".xls":
        return "assets/icons/chat/xls.svg";
      case ".xlsx":
        return "assets/icons/chat/xls.svg";
      case ".doc":
        return "assets/icons/chat/doc.svg";
      case ".docx":
        return "assets/icons/chat/doc.svg";
      case ".ppt":
        return "assets/icons/chat/ppt.svg";
      case ".pptx":
        return "assets/icons/chat/ppt.svg";
      case ".zip":
        return "assets/icons/chat/zip.svg";
      case ".rar":
        return "assets/icons/chat/zip.svg";
      case ".aac":
        return "assets/icons/chat/aac.svg";
      case ".mp3":
        return "assets/icons/chat/zip.mp3";
      default:
        return "assets/images/thumb.svg";
    }
  }

  void chatReply(SingleUserChatModel data) {
    repliedMsg = [];
    data.isReplied = true;
    isReplyMsg = true;
    repliedMsg.add(data);
    notifyListeners();
  }

  void closeMe() {
    repliedMsg = [];
    isReplyMsg = false;
    notifyListeners();
  }

  String dateFormte(DateTime data) {
    DateFormat f = DateFormat('hh:mm a dd MMM yyyy');
    f.format(data);
    return f.format(data);
  }

  Future<void> favoriteUser({required int userID, required int targetUserID, required bool fromSearch}) async {
    fav.FavoriteChatUser favoriteChatUser = await ChatApiClient().favUser(userID: userID, targetUserID: targetUserID);
    if (favoriteChatUser.response != null) {
      for (ChatUser user in searchedChats!) {
        if (user.id == favoriteChatUser.response!.targetUserId!) {
          user.isFav = favoriteChatUser.response!.isFav;
          dynamic contain = favUsersList!.where((ChatUser element) => element.id == favoriteChatUser.response!.targetUserId!);
          if (contain.isEmpty) {
            favUsersList.add(user);
          }
        }
      }

      for (ChatUser user in chatUsersList!) {
        if (user.id == favoriteChatUser.response!.targetUserId!) {
          user.isFav = favoriteChatUser.response!.isFav;
          dynamic contain = favUsersList!.where((ChatUser element) => element.id == favoriteChatUser.response!.targetUserId!);
          if (contain.isEmpty) {
            favUsersList.add(user);
          }
        }
      }
    }
    if (fromSearch) {
      for (ChatUser user in favUsersList) {
        if (user.id == targetUserID) {
          user.userLocalDownlaodedImage = null;
          user.isImageLoading = false;
          user.isImageLoaded = false;
        }
      }
    }
    notifyListeners();
  }

  Future<void> unFavoriteUser({required int userID, required int targetUserID}) async {
    fav.FavoriteChatUser favoriteChatUser = await ChatApiClient().unFavUser(userID: userID, targetUserID: targetUserID);

    if (favoriteChatUser.response != null) {
      for (ChatUser user in searchedChats!) {
        if (user.id == favoriteChatUser.response!.targetUserId!) {
          user.isFav = favoriteChatUser.response!.isFav;
        }
      }
      favUsersList.removeWhere(
        (ChatUser element) => element.id == targetUserID,
      );
    }

    for (ChatUser user in chatUsersList!) {
      if (user.id == favoriteChatUser.response!.targetUserId!) {
        user.isFav = favoriteChatUser.response!.isFav;
      }
    }

    notifyListeners();
  }

  void clearSelections() {
    searchedChats = pChatHistory;
    search.clear();
    isChatScreenActive = false;
    receiverID = 0;
    paginationVal = 0;
    message.text = '';
    isAttachmentMsg = false;
    repliedMsg = [];
    sFileType = "";
    isReplyMsg = false;
    isTextMsg = false;
    isVoiceMsg = false;
    notifyListeners();
  }

  void clearAll() {
    searchedChats = pChatHistory;
    search.clear();
    isChatScreenActive = false;
    receiverID = 0;
    paginationVal = 0;
    message.text = '';
    isTextMsg = false;
    isAttachmentMsg = false;
    isVoiceMsg = false;
    isReplyMsg = false;
    repliedMsg = [];
    sFileType = "";
  }

  void disposeData() {
    search.clear();
    isChatScreenActive = false;
    receiverID = 0;
    paginationVal = 0;
    message.text = '';
    isTextMsg = false;
    isAttachmentMsg = false;
    isVoiceMsg = false;
    isReplyMsg = false;
    repliedMsg = [];
    sFileType = "";
    deleteData();
    favUsersList.clear();
    searchedChats?.clear();
    pChatHistory?.clear();
    chatHubConnection.stop();
    AppState().chatDetails = null;
  }

  void deleteData() {
    List<ChatUser> exists = [], unique = [];
    if (searchedChats != null) exists.addAll(searchedChats!);
    exists.addAll(favUsersList!);
    Map<String, ChatUser> profileMap = {};
    for (ChatUser item in exists) {
      profileMap[item.email!] = item;
    }
    unique = profileMap.values.toList();
    for (ChatUser element in unique!) {
      deleteFile(element.id.toString());
    }
  }

  void getUserImages() async {
    List<String> emails = [];
    List<ChatUser> exists = [], unique = [];
    exists.addAll(searchedChats!);
    exists.addAll(favUsersList!);
    Map<String, ChatUser> profileMap = {};
    for (ChatUser item in exists) {
      profileMap[item.email!] = item;
    }
    unique = profileMap.values.toList();
    for (ChatUser element in unique!) {
      emails.add(await EmailImageEncryption().encrypt(val: element.email!));
    }

    List<ChatUserImageModel> chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails);
    for (ChatUser user in searchedChats!) {
      for (ChatUserImageModel uImage in chatImages) {
        if (user.email == uImage.email) {
          user.image = uImage.profilePicture ?? "";
          user.userLocalDownlaodedImage = await downloadImageLocal(uImage.profilePicture, user.id.toString());
          user.isImageLoading = false;
          user.isImageLoaded = true;
        }
      }
    }
    for (ChatUser favUser in favUsersList) {
      for (ChatUserImageModel uImage in chatImages) {
        if (favUser.email == uImage.email) {
          favUser.image = uImage.profilePicture ?? "";
          favUser.userLocalDownlaodedImage = await downloadImageLocal(uImage.profilePicture, favUser.id.toString());
          favUser.isImageLoading = false;
          favUser.isImageLoaded = true;
        }
      }
    }

    notifyListeners();
  }

  Future<File?> downloadImageLocal(String? encodedBytes, String userID) async {
    File? myfile;
    if (encodedBytes == null) {
      return myfile;
    } else {
      await deleteFile(userID);
      Uint8List decodedBytes = base64Decode(encodedBytes);
      Directory appDocumentsDirectory = await getApplicationDocumentsDirectory();
      String dirPath = '${appDocumentsDirectory.path}/chat_images';
      if (!await Directory(dirPath).exists()) {
        await Directory(dirPath).create();
        await File('$dirPath/.nomedia').create();
      }
      late File imageFile = File("$dirPath/$userID.jpg");
      imageFile.writeAsBytesSync(decodedBytes);
      return imageFile;
    }
  }

  Future deleteFile(String userID) async {
    Directory appDocumentsDirectory = await getApplicationDocumentsDirectory();
    String dirPath = '${appDocumentsDirectory.path}/chat_images';
    late File imageFile = File('$dirPath/$userID.jpg');
    if (await imageFile.exists()) {
      await imageFile.delete();
    }
  }

  Future<String> downChatMedia(Uint8List bytes, String ext) async {
    String dir = (await getApplicationDocumentsDirectory()).path;
    File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext);
    await file.writeAsBytes(bytes);
    return file.path;
  }

  void setMsgTune() async {
    JustAudio.AudioPlayer player = JustAudio.AudioPlayer();
    await player.setVolume(1.0);
    String audioAsset = "";
    if (Platform.isAndroid) {
      audioAsset = "assets/audio/pulse_tone_android.mp3";
    } else {
      audioAsset = "assets/audio/pulse_tune_ios.caf";
    }
    try {
      await player.setAsset(audioAsset);
      await player.load();
      player.play();
    } catch (e) {
      print("Error: $e");
    }
  }

  Future<void> getChatMedia(BuildContext context, {required String fileName, required String fileTypeName, required int fileTypeID}) async {
    Utils.showLoading(context);
    if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2) {
      Uint8List encodedString = await ChatApiClient().downloadURL(fileName: fileName, fileTypeDescription: getFileTypeDescription(fileTypeName));
      try {
        String path = await downChatMedia(encodedString, fileTypeName ?? "");
        Utils.hideLoading(context);
        OpenFile.open(path);
      } catch (e) {
        Utils.showToast("Cannot open file.");
      }
    }
  }

  void onNewChatConversion(List<Object?>? params) {
    dynamic items = params!.toList();
    chatUConvCounter = items[0]["singleChatCount"] ?? 0;
    notifyListeners();
  }

  Future invokeChatCounter({required int userId}) async {
    await chatHubConnection.invoke("GetChatCounversationCount", args: [userId]);
    return "";
  }

  void userTypingInvoke({required int currentUser, required int reciptUser}) async {
    await chatHubConnection.invoke("UserTypingAsync", args: [reciptUser, currentUser]);
  }

//////// Audio Recoding Work ////////////////////

  Future<void> initAudio({required int receiverId}) async {
    // final dir = Directory((Platform.isAndroid
    //     ? await getExternalStorageDirectory() //FOR ANDROID
    //     : await getApplicationSupportDirectory() //FOR IOS
    // )!
    appDirectory = await getApplicationDocumentsDirectory();
    String dirPath = '${appDirectory.path}/chat_audios';
    if (!await Directory(dirPath).exists()) {
      await Directory(dirPath).create();
      await File('$dirPath/.nomedia').create();
    }
    path = "$dirPath/${AppState().chatDetails!.response!.id}-$receiverID-${DateTime.now().microsecondsSinceEpoch}.aac";
    recorderController = RecorderController()
      ..androidEncoder = AndroidEncoder.aac
      ..androidOutputFormat = AndroidOutputFormat.mpeg4
      ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
      ..sampleRate = 6000
      ..updateFrequency = const Duration(milliseconds: 100)
      ..bitRate = 18000;
    playerController = PlayerController();
  }

  void disposeAudio() {
    isRecoding = false;
    isPlaying = false;
    isPause = false;
    isVoiceMsg = false;
    recorderController.dispose();
    playerController.dispose();
  }

  void startRecoding(BuildContext context) async {
    await Permission.microphone.request().then((PermissionStatus status) {
      if (status.isPermanentlyDenied) {
        Utils.confirmDialog(
          context,
          "The app needs microphone access to be able to record audio.",
          onTap: () {
            Navigator.of(context).pop();
            openAppSettings();
          },
        );
      } else if (status.isDenied) {
        Utils.confirmDialog(
          context,
          "The app needs microphone access to be able to record audio.",
          onTap: () {
            Navigator.of(context).pop();
            openAppSettings();
          },
        );
      } else if (status.isGranted) {
        sRecoding();
      } else {
        startRecoding(context);
      }
    });
  }

  void sRecoding() async {
    isVoiceMsg = true;
    recorderController.reset();
    await recorderController.record(path);
    _recodeDuration = 0;
    _startTimer();
    isRecoding = !isRecoding;
    notifyListeners();
  }

  Future<void> _startTimer() async {
    _timer?.cancel();
    _timer = Timer.periodic(const Duration(seconds: 1), (Timer t) async {
      _recodeDuration++;
      if (_recodeDuration <= 59) {
        applyCounter();
      } else {
        pauseRecoding();
      }
    });
  }

  void applyCounter() {
    buildTimer();
    notifyListeners();
  }

  Future<void> pauseRecoding() async {
    isPause = true;
    isPlaying = true;
    recorderController.pause();
    path = await recorderController.stop(false);
    File file = File(path!);
    file.readAsBytesSync();
    path = file.path;
    await playerController.preparePlayer(file.path, 1.0);
    _timer?.cancel();
    notifyListeners();
  }

  Future<void> deleteRecoding() async {
    _recodeDuration = 0;
    _timer?.cancel();
    if (path == null) {
      path = await recorderController.stop(true);
    } else {
      await recorderController.stop(true);
    }
    if (path != null && path!.isNotEmpty) {
      File delFile = File(path!);
      double fileSizeInKB = delFile.lengthSync() / 1024;
      double fileSizeInMB = fileSizeInKB / 1024;
      if (kDebugMode) {
        debugPrint("Deleted file size: ${delFile.lengthSync()}");
        debugPrint("Deleted file size in KB: " + fileSizeInKB.toString());
        debugPrint("Deleted file size in MB: " + fileSizeInMB.toString());
      }
      if (await delFile.exists()) {
        delFile.delete();
      }
      isPause = false;
      isRecoding = false;
      isPlaying = false;
      isVoiceMsg = false;
      notifyListeners();
    }
  }

  String buildTimer() {
    String minutes = _formatNum(_recodeDuration ~/ 60);
    String seconds = _formatNum(_recodeDuration % 60);
    return '$minutes : $seconds';
  }

  String _formatNum(int number) {
    String numberStr = number.toString();
    if (number < 10) {
      numberStr = '0' + numberStr;
    }
    return numberStr;
  }

  Future<File> downChatVoice(Uint8List bytes, String ext, SingleUserChatModel data) async {
    File file;
    try {
      String dirPath = '${(await getApplicationDocumentsDirectory()).path}/chat_audios';
      if (!await Directory(dirPath).exists()) {
        await Directory(dirPath).create();
        await File('$dirPath/.nomedia').create();
      }
      file = File("$dirPath/${data.currentUserId}-${data.targetUserId}-${DateTime.now().microsecondsSinceEpoch}" + ext);
      await file.writeAsBytes(bytes);
    } catch (e) {
      if (kDebugMode) {
        print(e);
      }
      file = File("");
    }
    return file;
  }

  void scrollToMsg(SingleUserChatModel data) {
    if (data.userChatReplyResponse != null && data.userChatReplyResponse!.userChatHistoryId != null) {
      int index = userChatHistory.indexWhere((SingleUserChatModel element) => element.userChatHistoryId == data.userChatReplyResponse!.userChatHistoryId);
      if (index >= 1) {
        double contentSize = scrollController.position.viewportDimension + scrollController.position.maxScrollExtent;
        double target = contentSize * index / userChatHistory.length;
        scrollController.position.animateTo(
          target,
          duration: const Duration(seconds: 1),
          curve: Curves.easeInOut,
        );
      }
    }
  }

  Future<void> getTeamMembers() async {
    teamMembersList = [];
    isLoading = true;
    if (AppState().getemployeeSubordinatesList.isNotEmpty) {
      getEmployeeSubordinatesList = AppState().getemployeeSubordinatesList;
      for (GetEmployeeSubordinatesList element in getEmployeeSubordinatesList) {
        if (element.eMPLOYEEEMAILADDRESS != null) {
          if (element.eMPLOYEEEMAILADDRESS!.isNotEmpty) {
            teamMembersList.add(
              ChatUser(
                id: int.parse(element.eMPLOYEENUMBER!),
                email: element.eMPLOYEEEMAILADDRESS,
                userName: element.eMPLOYEENAME,
                phone: element.eMPLOYEEMOBILENUMBER,
                userStatus: 0,
                unreadMessageCount: 0,
                isFav: false,
                isTyping: false,
                isImageLoading: false,
                image: element.eMPLOYEEIMAGE ?? "",
                isImageLoaded: element.eMPLOYEEIMAGE == null ? false : true,
                userLocalDownlaodedImage: element.eMPLOYEEIMAGE == null ? null : await downloadImageLocal(element.eMPLOYEEIMAGE ?? "", element.eMPLOYEENUMBER!),
              ),
            );
          }
        }
      }
    } else {
      getEmployeeSubordinatesList = await MyTeamApiClient().getEmployeeSubordinates("", "", "");
      AppState().setemployeeSubordinatesList = getEmployeeSubordinatesList;
      for (GetEmployeeSubordinatesList element in getEmployeeSubordinatesList) {
        if (element.eMPLOYEEEMAILADDRESS != null) {
          if (element.eMPLOYEEEMAILADDRESS!.isNotEmpty) {
            teamMembersList.add(
              ChatUser(
                id: int.parse(element.eMPLOYEENUMBER!),
                email: element.eMPLOYEEEMAILADDRESS,
                userName: element.eMPLOYEENAME,
                phone: element.eMPLOYEEMOBILENUMBER,
                userStatus: 0,
                unreadMessageCount: 0,
                isFav: false,
                isTyping: false,
                isImageLoading: false,
                image: element.eMPLOYEEIMAGE ?? "",
                isImageLoaded: element.eMPLOYEEIMAGE == null ? false : true,
                userLocalDownlaodedImage: element.eMPLOYEEIMAGE == null ? null : await downloadImageLocal(element.eMPLOYEEIMAGE ?? "", element.eMPLOYEENUMBER!),
              ),
            );
          }
        }
      }
    }

    for (ChatUser user in searchedChats!) {
      for (ChatUser teamUser in teamMembersList!) {
        if (user.id == teamUser.id) {
          teamUser.userStatus = user.userStatus;
        }
      }
    }

    isLoading = false;
    notifyListeners();
  }

  void inputBoxDirection(String val) {
    if (val.isNotEmpty) {
      isTextMsg = true;
      RegExp exp = RegExp("[a-zA-Z]");
      if (exp.hasMatch(val.substring(val.length - 1)) && val.substring(val.length - 1) != " ") {
        textDirection = Material.TextDirection.ltr;
        notifyListeners();
      } else if (val.substring(val.length - 1) != " " && !exp.hasMatch(val.substring(val.length - 1))) {
        textDirection = Material.TextDirection.rtl;
        notifyListeners();
      }
    } else {
      isTextMsg = false;
    }
  }

  Material.TextDirection getTextDirection(String v) {
    String str = v.trim();
    if (str.isEmpty) return Material.TextDirection.ltr;
    int firstUnit = str.codeUnitAt(0);
    if (firstUnit > 0x0600 && firstUnit < 0x06FF ||
        firstUnit > 0x0750 && firstUnit < 0x077F ||
        firstUnit > 0x07C0 && firstUnit < 0x07EA ||
        firstUnit > 0x0840 && firstUnit < 0x085B ||
        firstUnit > 0x08A0 && firstUnit < 0x08B4 ||
        firstUnit > 0x08E3 && firstUnit < 0x08FF ||
        firstUnit > 0xFB50 && firstUnit < 0xFBB1 ||
        firstUnit > 0xFBD3 && firstUnit < 0xFD3D ||
        firstUnit > 0xFD50 && firstUnit < 0xFD8F ||
        firstUnit > 0xFD92 && firstUnit < 0xFDC7 ||
        firstUnit > 0xFDF0 && firstUnit < 0xFDFC ||
        firstUnit > 0xFE70 && firstUnit < 0xFE74 ||
        firstUnit > 0xFE76 && firstUnit < 0xFEFC ||
        firstUnit > 0x10800 && firstUnit < 0x10805 ||
        firstUnit > 0x1B000 && firstUnit < 0x1B0FF ||
        firstUnit > 0x1D165 && firstUnit < 0x1D169 ||
        firstUnit > 0x1D16D && firstUnit < 0x1D172 ||
        firstUnit > 0x1D17B && firstUnit < 0x1D182 ||
        firstUnit > 0x1D185 && firstUnit < 0x1D18B ||
        firstUnit > 0x1D1AA && firstUnit < 0x1D1AD ||
        firstUnit > 0x1D242 && firstUnit < 0x1D244) {
      return Material.TextDirection.rtl;
    }
    return Material.TextDirection.ltr;
  }

  void openChatByNoti(BuildContext context) async {
    SingleUserChatModel nUser = SingleUserChatModel.fromJson(
      jsonDecode(await Utils.getStringFromPrefs("notificationData")),
    );
    Utils.saveStringFromPrefs("isAppOpendByChat", "false");
    Utils.saveStringFromPrefs("notificationData", "null");
    Future.delayed(const Duration(seconds: 1));
    for (ChatUser user in searchedChats!) {
      if (user.id == nUser.targetUserId) {
        Navigator.pushNamed(context, AppRoutes.chatDetailed, arguments: ChatDetailedScreenParams(user, false));
        return;
      } else {
        openChatByNoti(context);
      }
    }
  }
}
