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:mohem_flutter_app/api/chat/chat_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/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/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';

class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
  ScrollController scrollController = ScrollController();
  TextEditingController message = TextEditingController();
  TextEditingController search = TextEditingController();
  List<SingleUserChatModel> userChatHistory = [];
  List<ChatUser>? pChatHistory, searchedChats;
  String chatCID = '';
  bool isLoading = true;
  bool isChatScreenActive = false;
  int receiverID = 0;
  late File selectedFile;
  bool isFileSelected = false;
  String sFileType = "";
  bool isMsgReply = false;
  List<SingleUserChatModel> repliedMsg = [];
  List<ChatUser> favUsersList = [];
  int paginationVal = 0;
  bool currentUserTyping = false;
  int? cTypingUserId = 0;

  //Chat Home Page Counter
  int chatUConvCounter = 0;

  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=Web&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");
    }
  }

  void getUserRecentChats() async {
    if (chatHubConnection.state != HubConnectionState.Connected) {
      getUserAutoLoginToken().whenComplete(() async {
        await buildHubConnection();
        getUserRecentChats();
      });
      return;
    }
    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) {
        Utils.showToast("No More Data To Load");
      }
    } 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) {
    if (data != null) {
      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"];
      }
    }
    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) {
    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,
      Uint8List? voice,
      required bool isImageLoaded}) async {
    Uuid uuid = const Uuid();
    String contentNo = uuid.v4();
    String msg = message.text;
    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: getFileExtension(selectedFile.path).toString(),
              fileKind: "file",
              fileName: selectedFile.path.split("/").last,
              fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()),
            )
          : null,
      image: image,
      isImageLoaded: isImageLoaded,
      voice: voice,
    );
    if (kDebugMode) {
      logger.i("model data: " + jsonEncode(data));
    }
    userChatHistory.insert(0, data);
    isFileSelected = false;
    isMsgReply = 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 (!isFileSelected && !isMsgReply) {
      if (kDebugMode) {
        print("Normal Text Msg");
      }
      if (message.text == null || message.text.isEmpty) {
        return;
      }
      sendChatToServer(
          chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false, isImageLoaded: false, image: null);
    }
    if (isFileSelected && !isMsgReply) {
      if (kDebugMode) {
        logger.i("Normal Attachment 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: null,
          isReply: false,
          isImageLoaded: true,
          image: selectedFile.readAsBytesSync());
    }
    if (!isFileSelected && isMsgReply) {
      if (kDebugMode) {
        print("Normal Text To Text Reply");
      }
      if (message.text == null || 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);
    } // reply msg over image && normal
    if (isFileSelected && isMsgReply) {
      if (kDebugMode) {
        print("Reply With File");
      }
      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());
    }

    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;
        isFileSelected = true;
        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() {
    isFileSelected = 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;
    isMsgReply = true;
    repliedMsg.add(data);
    notifyListeners();
  }

  void closeMe() {
    repliedMsg = [];
    isMsgReply = 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}) 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);
          }
        }
      }
    }
    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,
      );
    }

    notifyListeners();
  }

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

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

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

  void deleteData() {
    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!) {
      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
  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;

//////// 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;
    recorderController.dispose();
    playerController.dispose();
  }

  void startRecoding() async {
    PermissionStatus status = await Permission.microphone.request();
    if (status.isDenied == true) {
      startRecoding();
    } else {
      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;
      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;
  }

  void playRecoding() async {
    isPlaying = true;
    await playerController.startPlayer(finishMode: FinishMode.pause);
  }

  void playOrPause() async {
    playerController.playerState == PlayerState.playing ? await playerController.pausePlayer() : playRecoding();
    notifyListeners();
  }

  void sendVoiceMessage(BuildContext context, {required int targetUserId, required int userStatus, required String userEmail, required String targetUserName}) async {
    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);
    sendVoiceMessageToServer(
      msgText: voiceFile.path!.split("/").last,
      chatEventId: 2,
      fileTypeId: getFileType(ext.toString()),
      targetUserId: targetUserId,
      targetUserName: targetUserName,
      isVoiceAttached: true,
      voice: voiceFile.readAsBytesSync(),
      userEmail: userEmail,
      userStatus: userStatus,
      chatReplyId: null,
      isAttachment: true,
      isReply: isMsgReply,
      voicFile: voiceFile,
    );
    notifyListeners();
  }

  Future<void> sendVoiceMessageToServer(
      {String? msgText,
      int? chatEventId,
      int? fileTypeId,
      int? targetUserId,
      String? targetUserName,
      bool? isVoiceAttached,
      Uint8List? voice,
      String? userEmail,
      int? userStatus,
      bool? isReply,
      bool? isAttachment,
      int? chatReplyId,
      File? voicFile}) async {
    Uuid uuid = const Uuid();
    String contentNo = uuid.v4();
    String msg = msgText!;
    SingleUserChatModel data = SingleUserChatModel(
      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: getFileExtension(voicFile!.path).toString(),
              fileKind: "file",
              fileName: msgText,
              fileTypeDescription: getFileTypeDescription(getFileExtension(voicFile!.path).toString()),
            )
          : null,
      image: null,
      isImageLoaded: false,
      voice: voice,
    );
    userChatHistory.insert(0, data);
    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)]);

    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 playVoice(
    BuildContext context, {
    required SingleUserChatModel data,
  }) async {
    Utils.showLoading(context);
    Uint8List encodedString = await ChatApiClient().downloadURL(fileName: data.contant!, fileTypeDescription: getFileTypeDescription(data.fileTypeResponse!.fileTypeName ?? ""));
    try {
      String path = await downChatVoice(encodedString, data.fileTypeResponse!.fileTypeName ?? "", data);
      File file = File(path!);
      file.readAsBytesSync();
      Utils.hideLoading(context);
      await data.voiceController!.preparePlayer(file.path, 1.0);
      data.voiceController!.startPlayer(finishMode: FinishMode.pause);
      notifyListeners();
    } catch (e) {
      Utils.showToast("Cannot open file.");
    }
  }

  void stopPlaying(BuildContext context, {required SingleUserChatModel data}) async {
    data.voiceController!.stopPlayer();
    notifyListeners();
  }

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

//     data.scrollController.animateTo(
//               data.scrollController.position.maxScrollExtent,
//               duration: const Duration(milliseconds: 100),
//               curve: Curves.easeOut,
//             );
}
