import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:openim_live/openim_live.dart';

import '../../company/feature_flags.dart';
import '../../company/rtc/call_permissions.dart';
import '../../company/rtc/call_session_guard.dart';
import '../../company/rtc/rtc_token_api.dart';
import '../im_callback.dart';

class IMController extends GetxController with IMCallback, OpenIMLive {
  late Rx<UserFullInfo> userInfo;
  late String atAllTag;
  final _rtcTokenApi = RtcTokenApi();
  final callGuard = CallSessionGuard();

  @override
  void onClose() {
    callGuard.dispose();
    super.close();
    onCloseLive();
    super.onClose();
  }

  @override
  Future<SignalingCertificate> getRtcCertificate(String roomID, String userID) {
    final token = DataSp.imToken;
    if (token == null || token.isEmpty) {
      return Future.error(StateError('登录状态已失效，请重新登录'));
    }
    return _rtcTokenApi.getToken(room: roomID, identity: userID, authToken: token);
  }

  @override
  void onLiveSessionClosed() {
    callGuard.dispose();
  }

  @override
  void onLiveConnected() {
    callGuard.markConnected();
  }

  @override
  void receiveNewInvitation(SignalingInfo info) {
    if (!FeatureFlags.livekitCall) return;
    final invitation = info.invitation;
    final roomID = invitation?.roomID ?? '';
    if (invitation?.mediaType != 'audio' || invitation?.sessionType != ConversationType.single) {
      onTapReject(info);
      return;
    }
    if (!callGuard.acceptSignaling(customType: CustomMessageType.callingInvite, roomID: roomID)) {
      return;
    }
    if (isBusy || callGuard.isBusy) {
      onTapReject(info);
      return;
    }
    if (callGuard.startIncoming(roomID) != null) {
      onTapReject(info);
      return;
    }
    super.receiveNewInvitation(info);
  }

  @override
  void call({
    required CallObj callObj,
    required CallType callType,
    CallState callState = CallState.call,
    String? roomID,
    String? inviterUserID,
    required List<String> inviteeUserIDList,
    String? groupID,
    SignalingCertificate? credentials,
  }) async {
    if (!FeatureFlags.livekitCall) return;
    if (callType != CallType.audio || callObj != CallObj.single) {
      IMViews.showToast('暂只支持一对一语音通话');
      return;
    }
    if (isBusy || callGuard.isBusy) {
      IMViews.showToast(StrRes.callingBusy);
      return;
    }
    final granted = await CallPermissions.ensureMicrophone();
    if (!granted) return;
    callGuard.startOutgoing(roomID ?? '');
    super.call(
      callObj: callObj,
      callType: callType,
      callState: callState,
      roomID: roomID,
      inviterUserID: inviterUserID,
      inviteeUserIDList: inviteeUserIDList,
      groupID: groupID,
      credentials: credentials,
    );
  }

  @override
  Future<SignalingCertificate> onTapPickup(SignalingInfo signaling) async {
    final granted = await CallPermissions.ensureMicrophone();
    if (!granted) {
      await onTapReject(signaling);
      return Future.error(StateError('microphone denied'));
    }
    return super.onTapPickup(signaling);
  }

  @override
  void onInit() async {
    super.onInit();
    onInitLive();
    WidgetsBinding.instance.addPostFrameCallback((_) => initOpenIM());
  }

  void initOpenIM() async {
    final initialized = await OpenIM.iMManager.initSDK(
      platformID: IMUtils.getPlatform(),
      apiAddr: Config.imApiUrl,
      wsAddr: Config.imWsUrl,
      dataDir: Config.cachePath,
      logLevel: Config.logLevel,
      logFilePath: Config.cachePath,
      listener: OnConnectListener(
        onConnecting: () {
          imSdkStatus(IMSdkStatus.connecting);
        },
        onConnectFailed: (code, error) {
          imSdkStatus(IMSdkStatus.connectionFailed);
        },
        onConnectSuccess: () {
          imSdkStatus(IMSdkStatus.connectionSucceeded);
        },
        onKickedOffline: kickedOffline,
        onUserTokenExpired: kickedOffline,
        onUserTokenInvalid: userTokenInvalid,
      ),
    );

    OpenIM.iMManager
      ..setUploadLogsListener(OnUploadLogsListener(onUploadProgress: uploadLogsProgress))
      ..userManager.setUserListener(OnUserListener(
          onSelfInfoUpdated: (u) {
            selfInfoUpdated(u);

            userInfo.update((val) {
              val?.nickname = u.nickname;
              val?.faceURL = u.faceURL;

              val?.remark = u.remark;
              val?.ex = u.ex;
              val?.globalRecvMsgOpt = u.globalRecvMsgOpt;
            });
          },
          onUserStatusChanged: userStausChanged))
      ..messageManager.setAdvancedMsgListener(OnAdvancedMsgListener(
        onRecvC2CReadReceipt: recvC2CMessageReadReceipt,
        onRecvNewMessage: recvNewMessage,
        onNewRecvMessageRevoked: recvMessageRevoked,
        onRecvOfflineNewMessage: recvOfflineMessage,
        onRecvOnlineOnlyMessage: (msg) {
          if (msg.isCustomType) {
            final data = msg.customElem!.data;
            final map = jsonDecode(data!);
            final customType = map['customType'];
            if (customType == CustomMessageType.callingInvite ||
                customType == CustomMessageType.callingAccept ||
                customType == CustomMessageType.callingReject ||
                customType == CustomMessageType.callingCancel ||
                customType == CustomMessageType.callingHungup) {
              if (!FeatureFlags.livekitCall) return;
              final signaling = SignalingInfo(invitation: InvitationInfo.fromJson(map['data']));
              signaling.userID = signaling.invitation?.inviterUserID;
              final roomID = signaling.invitation?.roomID ?? '';
              if (customType != CustomMessageType.callingInvite &&
                  !callGuard.acceptSignaling(customType: customType as int, roomID: roomID)) {
                return;
              }

              switch (customType) {
                case CustomMessageType.callingInvite:
                  receiveNewInvitation(signaling);
                  break;
                case CustomMessageType.callingAccept:
                  inviteeAccepted(signaling);
                  break;
                case CustomMessageType.callingReject:
                  inviteeRejected(signaling);
                  break;
                case CustomMessageType.callingCancel:
                  invitationCancelled(signaling);
                  break;
                case CustomMessageType.callingHungup:
                  beHangup(signaling);
                  break;
              }
            }
          }
        },
      ))
      ..messageManager.setMsgSendProgressListener(OnMsgSendProgressListener(
        onProgress: progressCallback,
      ))
      ..messageManager.setCustomBusinessListener(OnCustomBusinessListener(
        onRecvCustomBusinessMessage: recvCustomBusinessMessage,
      ))
      ..friendshipManager.setFriendshipListener(OnFriendshipListener(
        onBlackAdded: blacklistAdded,
        onBlackDeleted: blacklistDeleted,
        onFriendApplicationAccepted: friendApplicationAccepted,
        onFriendApplicationAdded: friendApplicationAdded,
        onFriendApplicationDeleted: friendApplicationDeleted,
        onFriendApplicationRejected: friendApplicationRejected,
        onFriendInfoChanged: friendInfoChanged,
        onFriendAdded: friendAdded,
        onFriendDeleted: friendDeleted,
      ))
      ..conversationManager.setConversationListener(OnConversationListener(
          onConversationChanged: conversationChanged,
          onNewConversation: newConversation,
          onTotalUnreadMessageCountChanged: totalUnreadMsgCountChanged,
          onInputStatusChanged: inputStateChanged,
          onSyncServerFailed: (reInstall) {
            imSdkStatus(IMSdkStatus.syncFailed, reInstall: reInstall ?? false);
          },
          onSyncServerFinish: (reInstall) {
            imSdkStatus(IMSdkStatus.syncEnded, reInstall: reInstall ?? false);
            if (Platform.isAndroid) {
              Permissions.request([Permission.systemAlertWindow]);
            }
          },
          onSyncServerStart: (reInstall) {
            imSdkStatus(IMSdkStatus.syncStart, reInstall: reInstall ?? false);
          },
          onSyncServerProgress: (progress) {
            imSdkStatus(IMSdkStatus.syncProgress, progress: progress);
          }))
      ..groupManager.setGroupListener(OnGroupListener(
        onGroupApplicationAccepted: groupApplicationAccepted,
        onGroupApplicationAdded: groupApplicationAdded,
        onGroupApplicationDeleted: groupApplicationDeleted,
        onGroupApplicationRejected: groupApplicationRejected,
        onGroupInfoChanged: groupInfoChanged,
        onGroupMemberAdded: groupMemberAdded,
        onGroupMemberDeleted: groupMemberDeleted,
        onGroupMemberInfoChanged: groupMemberInfoChanged,
        onJoinedGroupAdded: joinedGroupAdded,
        onJoinedGroupDeleted: joinedGroupDeleted,
      ));

    initializedSubject.sink.add(initialized);
  }

  Future login(String userID, String token) async {
    try {
      var user = await OpenIM.iMManager.login(
        userID: userID,
        token: token,
        defaultValue: () async => UserInfo(userID: userID),
      );
      ApiService().setToken(token);
      userInfo = UserFullInfo.fromJson(user.toJson()).obs;
      _queryMyFullInfo();
      _queryAtAllTag();
    } catch (e, s) {
      Logger.print('e: $e  s:$s');
      await _handleLoginRepeatError(e);

      return Future.error(e, s);
    }
  }

  Future logout() {
    return OpenIM.iMManager.logout();
  }

  void _queryAtAllTag() async {
    atAllTag = OpenIM.iMManager.conversationManager.atAllTag;
  }

  void _queryMyFullInfo() async {
    final data = await Apis.queryMyFullInfo();
    if (data is UserFullInfo) {
      userInfo.update((val) {
        val?.allowAddFriend = data.allowAddFriend;
        val?.allowBeep = data.allowBeep;
        val?.allowVibration = data.allowVibration;
        val?.nickname = data.nickname;
        val?.faceURL = data.faceURL;
        val?.phoneNumber = data.phoneNumber;
        val?.email = data.email;
        val?.birth = data.birth;
        val?.gender = data.gender;
      });
    }
  }

  _handleLoginRepeatError(e) async {
    if (e is PlatformException && (e.code == "13002" || e.code == '1507')) {
      await logout();
      await DataSp.removeLoginCertificate();
    }
  }
}
