import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:logging/logging.dart';
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/utils.dart';
import 'package:mohem_flutter_app/main.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 login;
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: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;
  late File selectedFile;
  bool isFileSelected = false;
  String sFileType = "";
  bool isMsgReply = false;
  List<SingleUserChatModel> repliedMsg = [];
  List<ChatUser> favUsersList = [];
  int paginationVal = 0;

  void registerEvents() {
    hubConnection.on("OnUpdateUserStatusAsync", changeStatus);
    hubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
    //   hubConnection.on("OnSeenChatUserAsync", onChatSeen);
    //hubConnection.on("OnUserTypingAsync", onUserTyping);
    hubConnection.on("OnUserCountAsync", userCountAsync);
    hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow);
    hubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered);
    hubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus);
  }

  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(),
      ),
    );
    notifyListeners();
  }

  Future invokeUserChatHistoryNotDeliveredAsync({required int userId}) async {
    await hubConnection.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;
    Response response = await ChatApiClient().getSingleUserChatHistory(
      senderUID: senderUID,
      receiverUID: receiverUID,
      loadMore: loadMore,
      paginationVal: paginationVal,
    );
    if (response.statusCode == 204) {
      if (isNewChat) {
        userChatHistory = [];
      } else if (loadMore) {
        // userChatHistory = [];
        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();
    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 (element.isSeen != null) {
          if (!element.isSeen!) {
            dynamic data = [
              {
                "userChatHistoryId": element.userChatHistoryId,
                "TargetUserId": element.targetUserId,
                "isDelivered": true,
                "isSeen": true,
              }
            ];
            updateUserChatHistoryStatusAsync(data);
          }
        }
      }
      for (ChatUser element in searchedChats!) {
        if (element.id == receiverID) {
          element.unreadMessageCount = 0;
          notifyListeners();
        }
      }
    }
  }

  void updateUserChatHistoryStatusAsync(List data) {
    hubConnection.invoke(
      "UpdateUserChatHistoryStatusAsync",
      args: [data],
    );
  }

  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 {
      StreamedResponse response = await ChatApiClient().uploadMedia(userId, file);
      if (response.statusCode == 200) {
        result = jsonDecode(
          await response.stream.bytesToString(),
        );
      } else {
        result = [];
      }
    } catch (e) {
      print(e);
    }
    ;
    return result;
  }

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

  void onChatSeen(List<Object?>? args) {
    dynamic items = args!.toList();
    logger.d("---------------------------------Chat Seen  -------------------------------------");
    logger.d(items);
    // 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();
    print("---------------------------------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();
    logger.d(items);
    for (dynamic item in items[0]) {
      searchedChats!.forEach(
        (ChatUser element) {
          if (element.id == item["currentUserId"]) {
            int? val = element.unreadMessageCount ?? 0;
            element.unreadMessageCount = val! + 1;
          }
          element.isLoadingCounter = false;
        },
      );
    }
    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 = [];
    List<SingleUserChatModel> 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.currentUserId = temp.first.targetUserId;
      data.first.currentUserName = temp.first.targetUserName;
    }
    userChatHistory.insert(0, data.first);
    var list = [
      {
        "userChatHistoryId": data.first.userChatHistoryId,
        "TargetUserId": data.first.targetUserId,
        "isDelivered": true,
        "isSeen": isChatScreenActive ? true : false,
      }
    ];
    updateUserChatHistoryStatusAsync(list);
    notifyListeners();
  }

  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;
      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";
      default:
        return "";
    }
  }

  Future<void> sendChatToServer(
      {required int chatEventId, required fileTypeId, required int targetUserId, required String targetUserName, required chatReplyId, required bool isAttachment, required bool isReply}) async {
    Uuid uuid = const Uuid();

    var msg = message.text;
    SingleUserChatModel data = SingleUserChatModel(
      chatEventId: chatEventId,
      chatSource: 1,
      contant: msg,
      contantNo: uuid.v4(),
      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: getFileType(getFileExtension(selectedFile.path).toString()),
              fileKind: getFileExtension(selectedFile.path),
              fileName: selectedFile.path.split("/").last,
              fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()),
            )
          : null,
    );
    userChatHistory.insert(0, data);
    isFileSelected = false;
    isMsgReply = false;
    sFileType = "";
    message.clear();
    notifyListeners();

    String chatData =
        '{"contant":"$msg","contantNo":"${uuid.v4()}","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 hubConnection.invoke("AddChatUserAsync", args: <Object>[json.decode(chatData)]);
  }

  void sendChatMessage(int targetUserId, String targetUserName, BuildContext context) async {
    dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
    if (contain.isEmpty) {
      searchedChats!.add(
        ChatUser(
          id: targetUserId,
          userName: targetUserName,
        ),
      );
      notifyListeners();
    }
    if (!isFileSelected && !isMsgReply) {
      if (message.text == null || message.text.isEmpty) {
        return;
      }
      sendChatToServer(chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false);
    }
    if (isFileSelected && !isMsgReply) {
      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);
    }
    if (!isFileSelected && isMsgReply) {
      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);
    }
    if (isFileSelected && isMsgReply) {
      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,
      );
    }
  }

  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 {
      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";
      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;
          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() {
    print("Hereee i am ");
    searchedChats = pChatHistory;
    search.clear();
    isChatScreenActive = false;
    paginationVal = 0;
    message.text = '';
    isFileSelected = false;
    repliedMsg = [];
    sFileType = "";
    isMsgReply = false;
    notifyListeners();
  }

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

  // void scrollListener() {
  //   _firstAutoscrollExecuted = true;
  //   if (scrollController.hasClients && scrollController.position.pixels == scrollController.position.maxScrollExtent) {
  //     _shouldAutoscroll = true;
  //   } else {
  //     _shouldAutoscroll = false;
  //   }
  // }
  //
  // void scrollToBottom() {
  //   scrollController.animateTo(
  //     scrollController.position.maxScrollExtent + 100,
  //     duration: const Duration(milliseconds: 500),
  //     curve: Curves.easeIn,
  //   );
  // }

  void msgScroll() {
    scrollController.animateTo(
      scrollController.position.minScrollExtent - 100,
      duration: const Duration(milliseconds: 500),
      curve: Curves.easeIn,
    );
  }

// void getUserChatHistoryNotDeliveredAsync({required int userId}) async {
//   try {
//     await hubConnection.invoke("GetUserChatHistoryNotDeliveredAsync", args: [userId]);
//   } finally {
//     hubConnection.off("GetUserChatHistoryNotDeliveredAsync", method: chatNotDelivered);
//   }
// }
}
