import 'dart:async';

import 'package:flutter/material.dart';

import 'package:cached_network_image/cached_network_image.dart';
import 'package:get_it_mixin/get_it_mixin.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:together_mobile/common/constants.dart';
import 'package:together_mobile/database/hive_database.dart';
import 'package:together_mobile/request/message.dart';

import 'package:together_mobile/screens/chat/components/group_chat_chat_tile.dart';
import 'package:together_mobile/utils/ws_receive_callback.dart';
import 'components/friend_chat_tile.dart';
import 'components/add_menu.dart';
import 'package:together_mobile/database/box_type.dart';
import 'package:together_mobile/models/websocket_model.dart';
import 'package:together_mobile/utils/format_datetime.dart';
import 'package:together_mobile/models/contact_model.dart';
import 'package:together_mobile/models/apply_list_model.dart';
import 'package:together_mobile/request/apply.dart';
import 'package:together_mobile/request/server.dart';
import 'package:together_mobile/request/contact.dart';
import 'package:together_mobile/models/user_model.dart';
import 'package:together_mobile/models/init_get_it.dart';
import 'package:together_mobile/request/user_profile.dart';

class ChatScreen extends StatefulWidget with GetItStatefulWidgetMixin {
  ChatScreen({super.key});

  @override
  State<ChatScreen> createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> with GetItStateMixin {
  Future<bool> _initData() async {
    if (!getIt.get<UserProfile>().isInitialised) {
      await HiveDatabase.init();

      getIt.get<WebSocketManager>().connect(getIt.get<UserAccount>().id, false);

      String userId = getIt.get<UserAccount>().id;

      List<Map<String, dynamic>> res = await Future.wait([
        getMyProfile(userId),
        getApplyList(userId),
        getContact(userId),
      ]);

      await getIt.get<UserProfile>().init(res[0]['data']);

      if (res[1]['code'] == 10600) {
        getIt.get<ApplyList>().init(res[1]['data']);
      }

      if (res[2]['code'] == 10700) {
        getIt.get<Contact>().init(res[2]['data']);
      }

      Map<String, dynamic> contactAcctProfRes = await getContactAccountProfiles(
        getIt.get<Contact>().friends.keys.toList(),
        getIt.get<Contact>().groupChats.keys.toList(),
      );

      if (contactAcctProfRes['code'] == 10700) {
        getIt.get<ContactAccountProfile>().init(contactAcctProfRes['data']);
      }

      await _getUnreceivedMsg(userId);
    }

    return true;
  }

  Future<void> _getUnreceivedMsg(String userId) async {
    final res = await getUnreceivedMsg(userId);

    if (res['code'] == 10900) {
      for (var msg in res['data']) {
        if (msg['event'] == 'friend-chat-msg') {
          receiveFriendMsg(msg, false);
        } else if (msg['event'] == 'group-chat-msg') {
          receiveGroupChatMsg(msg, false);
        }
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    SocketStatus socketStatus = watchOnly(
      (WebSocketManager wm) => wm.socketStatus,
    );

    return FutureBuilder(
      future: _initData(),
      builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
        if (snapshot.hasData) {
          return Scaffold(
            appBar: AppBar(
              leading: CircleAvatar(
                backgroundImage: CachedNetworkImageProvider(
                  '$userAvatarsUrl/${getIt.get<UserProfile>().avatar}',
                ),
              ),
              title: Text(getIt.get<UserProfile>().nickname),
              centerTitle: true,
              actions: [
                IconButton(
                  onPressed: () {},
                  splashRadius: 20,
                  icon: const Icon(Icons.search),
                ),
                const AddMenu(),
              ],
            ),
            // Use ListView.builder because it renders list element on demand
            body: Column(
              children: [
                if (socketStatus == SocketStatus.reconnecting)
                  Container(
                    height: 35,
                    width: double.maxFinite,
                    alignment: Alignment.center,
                    color: kErrorColor.withOpacity(0.35),
                    child: const Text(
                      '网络中断,正在重新连接......',
                      style: TextStyle(
                        fontSize: 16,
                        color: kErrorColor,
                      ),
                    ),
                  ),
                if (socketStatus == SocketStatus.error)
                  Container(
                    height: 35,
                    width: double.maxFinite,
                    alignment: Alignment.center,
                    color: kErrorColor.withOpacity(0.35),
                    child: const Text(
                      '网络异常,请下划尝试重新连接!',
                      style: TextStyle(
                        fontSize: 16,
                        color: kErrorColor,
                      ),
                    ),
                  ),
                Expanded(
                  child: RefreshIndicator(
                    onRefresh: () async {
                      if (socketStatus == SocketStatus.reconnecting) {
                        return;
                      }

                      String userId = getIt.get<UserAccount>().id;

                      if (socketStatus != SocketStatus.connected) {
                        getIt.get<WebSocketManager>().connect(userId, true);

                        await Future.doWhile(() async {
                          SocketStatus socketStatus =
                              get<WebSocketManager>().socketStatus;
                          await Future.delayed(
                            const Duration(milliseconds: 500),
                          );
                          return socketStatus == SocketStatus.reconnecting;
                        });
                      }

                      socketStatus = get<WebSocketManager>().socketStatus;

                      if (socketStatus == SocketStatus.connected) {
                        await _getUnreceivedMsg(userId);
                      }
                    },
                    child: ValueListenableBuilder(
                      valueListenable:
                          Hive.box<ChatSetting>('chat_setting').listenable(),
                      builder: (context, Box<ChatSetting> box, _) {
                        final List<ChatSetting> openedChat = box.values
                            .where((element) => element.isOpen)
                            .toList();

                        // latestMsg on the top
                        openedChat.sort(
                          (a, b) =>
                              b.latestDateTime.compareTo(a.latestDateTime),
                        );

                        if (openedChat.isEmpty) {
                          return LayoutBuilder(builder: (context, constrains) {
                            return ListView(
                              itemExtent: constrains.maxHeight / 3,
                              children: const [
                                SizedBox(),
                                Center(
                                  child: Text(
                                    '没有最新消息',
                                    style: TextStyle(
                                      fontSize: 18,
                                      letterSpacing: 4.0,
                                    ),
                                  ),
                                ),
                                SizedBox(),
                              ],
                            );
                          });
                        } else {
                          return ListView.builder(
                            physics: const BouncingScrollPhysics(
                              parent: AlwaysScrollableScrollPhysics(),
                            ),
                            itemCount: openedChat.length,
                            itemBuilder: (BuildContext context, int index) {
                              String contactId = openedChat[index].contactId;
                              String showedTime = formatTileDateTime(
                                openedChat[index].latestDateTime,
                              );
                              int unreadCount = openedChat[index].unreadCount;

                              return ValueListenableBuilder(
                                valueListenable:
                                    Hive.box<MessageT>('message_$contactId')
                                        .listenable(),
                                builder: (context, messageTBox, _) {
                                  int length = messageTBox.length;
                                  if (length > 0) {
                                    MessageT messageT =
                                        messageTBox.getAt(length - 1)!;
                                    if (openedChat[index].type == 0) {
                                      return FriendChatTile(
                                        key: ValueKey(contactId),
                                        contactId: contactId,
                                        senderId: messageT.senderId,
                                        messageType: messageT.type,
                                        text: messageT.text,
                                        attachments: messageT.attachments,
                                        dateTime: showedTime,
                                        isShowTime: messageT.isShowTime,
                                        unreadCount: unreadCount,
                                      );
                                    } else {
                                      return GroupChatChatTile(
                                        key: ValueKey(contactId),
                                        index: index,
                                        contactId: contactId,
                                        senderId: messageT.senderId,
                                        messageType: messageT.type,
                                        text: messageT.text,
                                        attachments: messageT.attachments,
                                        dateTime: showedTime,
                                        isShowTime: messageT.isShowTime,
                                        unreadCount: unreadCount,
                                      );
                                    }
                                  } else {
                                    return const SizedBox();
                                  }
                                },
                              );
                            },
                          );
                        }
                      },
                    ),
                  ),
                ),
              ],
            ),
          );
        } else {
          return const Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SizedBox(
                  width: 60,
                  height: 60,
                  child: CircularProgressIndicator(),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: Text('Loading data....'),
                )
              ],
            ),
          );
        }
      },
    );
  }
}