//
//  ExtSdkClientWrapper.m
//
//
//  Created by 杜洁鹏 on 2019/10/8.
//

#import "ExtSdkClientWrapper.h"
#import "ExtSdkChatManagerWrapper.h"
#import "ExtSdkChatThreadManagerWrapper.h"
#import "ExtSdkChatroomManagerWrapper.h"
#import "ExtSdkContactManagerWrapper.h"
#import "ExtSdkConversationWrapper.h"
#import "ExtSdkGroupManagerWrapper.h"
#import "ExtSdkMethodTypeObjc.h"
#import "ExtSdkPresenceManagerWrapper.h"
#import "ExtSdkPushManagerWrapper.h"
#import "ExtSdkThreadUtilObjc.h"
#import "ExtSdkToJson.h"
#import "ExtSdkUserInfoManagerWrapper.h"
#import <UserNotifications/UserNotifications.h>

@interface ExtSdkClientWrapper () <AgoraChatClientDelegate, AgoraChatMultiDevicesDelegate>
@end

@implementation ExtSdkClientWrapper

+ (nonnull instancetype)getInstance {
    static ExtSdkClientWrapper *instance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
      instance = [[ExtSdkClientWrapper alloc] init];
    });
    return instance;
}

#pragma mark - Actions

- (void)initSDKWithDict:(NSDictionary *)param
         withMethodType:(NSString *)aChannelName
                 result:(nonnull id<ExtSdkCallbackObjc>)result {

    AgoraChatOptions *options = [AgoraChatOptions fromJsonObject:param];
    if (nil == options) {
        AgoraChatError *e = [AgoraChatError errorWithDescription:@"params parse error."
                                              code:1];
        [self onResult:result
            withMethodType:ExtSdkMethodKeyInit
                 withError:e
                withParams:nil];
        return;
    }
    options.enableConsoleLog = options.enableConsoleLog;
    [AgoraChatClient.sharedClient initializeSDKWithOptions:options];
    [AgoraChatClient.sharedClient removeDelegate:self];
    [AgoraChatClient.sharedClient addDelegate:self delegateQueue:nil];
    [AgoraChatClient.sharedClient removeMultiDevicesDelegate:self];
    [AgoraChatClient.sharedClient addMultiDevicesDelegate:self delegateQueue:nil];

    [ExtSdkChatManagerWrapper.getInstance initSdk];
    [ExtSdkChatroomManagerWrapper.getInstance initSDK];
    [ExtSdkContactManagerWrapper.getInstance initSdk];
    [ExtSdkGroupManagerWrapper.getInstance initSdk];
    [ExtSdkPresenceManagerWrapper.getInstance initSdk];
    [ExtSdkChatThreadManagerWrapper.getInstance initSDK];
    [ExtSdkPushManagerWrapper.getInstance initSDK];

    [self onResult:result
        withMethodType:ExtSdkMethodKeyInit
             withError:nil
            withParams:nil];
}

- (void)getToken:(NSDictionary *)param
    withMethodType:(NSString *)aChannelName
            result:(nonnull id<ExtSdkCallbackObjc>)result {
    [self onResult:result
        withMethodType:ExtSdkMethodKeyGetToken
             withError:nil
            withParams:AgoraChatClient.sharedClient.accessUserToken];
}

- (void)createAccount:(NSDictionary *)param
       withMethodType:(NSString *)aChannelName
               result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    NSString *username = param[@"username"];
    NSString *password = param[@"password"];
    [AgoraChatClient.sharedClient
        registerWithUsername:username
                    password:password
                  completion:^(NSString *aUsername, AgoraChatError *aError) {
                    [weakSelf onResult:result
                        withMethodType:ExtSdkMethodKeyCreateAccount
                             withError:aError
                            withParams:aUsername];
                  }];
}

- (void)login:(NSDictionary *)param
    withMethodType:(NSString *)aChannelName
            result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    NSString *username = param[@"username"];
    NSString *pwdOrToken = param[@"pwdOrToken"];
    BOOL isPwd = [param[@"isPassword"] boolValue];

    if (isPwd) {
        [AgoraChatClient.sharedClient
            loginWithUsername:username
                     password:pwdOrToken
                   completion:^(NSString *aUsername, AgoraChatError *aError) {
                     [weakSelf onResult:result
                         withMethodType:ExtSdkMethodKeyLogin
                              withError:aError
                             withParams:@{
                                 @"username" : aUsername,
                                 @"token" :
                                     AgoraChatClient.sharedClient.accessUserToken
                             }];
                   }];
    } else {
        [AgoraChatClient.sharedClient
            loginWithUsername:username
                        token:pwdOrToken
                   completion:^(NSString *aUsername, AgoraChatError *aError) {
                     [weakSelf onResult:result
                         withMethodType:ExtSdkMethodKeyLogin
                              withError:aError
                             withParams:@{
                                 @"username" : aUsername,
                                 @"token" :
                                     AgoraChatClient.sharedClient.accessUserToken
                             }];
                   }];
    }
}

- (void)logout:(NSDictionary *)param
    withMethodType:(NSString *)aChannelName
            result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    BOOL unbindToken = [param[@"unbindToken"] boolValue];
    if (YES == unbindToken &&
        nil == AgoraChatClient.sharedClient.options.apnsCertName) {
        unbindToken = NO;
    }
    [AgoraChatClient.sharedClient logout:unbindToken
                       completion:^(AgoraChatError *aError) {
                         [weakSelf onResult:result
                             withMethodType:ExtSdkMethodKeyLogout
                                  withError:aError
                                 withParams:@(!aError)];
                       }];
}

- (void)changeAppKey:(NSDictionary *)param
      withMethodType:(NSString *)aChannelName
              result:(nonnull id<ExtSdkCallbackObjc>)result {
    NSString *appKey = param[@"appKey"];
    AgoraChatError *aError = [AgoraChatClient.sharedClient changeAppkey:appKey];
    [self onResult:result
        withMethodType:ExtSdkMethodKeyChangeAppKey
             withError:aError
            withParams:@(!aError)];
}

- (void)changeAppId:(NSDictionary *)param
     withMethodType:(NSString *)aChannelName
             result:(nonnull id<ExtSdkCallbackObjc>)result {
    NSString *appId = param[@"appId"];
    AgoraChatError *aError = [AgoraChatClient.sharedClient changeAppId:appId];
    [self onResult:result
        withMethodType:aChannelName
             withError:aError
            withParams:@(!aError)];
}

- (void)getCurrentUser:(NSDictionary *)param
        withMethodType:(NSString *)aChannelName
                result:(nonnull id<ExtSdkCallbackObjc>)result {
    NSString *username = AgoraChatClient.sharedClient.currentUsername;
    [self onResult:result
        withMethodType:ExtSdkMethodKeyGetCurrentUser
             withError:nil
            withParams:username];
}

- (void)uploadLog:(NSDictionary *)param
    withMethodType:(NSString *)aChannelName
            result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    [AgoraChatClient.sharedClient
        uploadDebugLogToServerWithCompletion:^(AgoraChatError *aError) {
          [weakSelf onResult:result
              withMethodType:ExtSdkMethodKeyUploadLog
                   withError:aError
                  withParams:nil];
        }];
}

- (void)compressLogs:(NSDictionary *)param
      withMethodType:(NSString *)aChannelName
              result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    [AgoraChatClient.sharedClient
        getLogFilesPathWithCompletion:^(NSString *aPath, AgoraChatError *aError) {
          [weakSelf onResult:result
              withMethodType:ExtSdkMethodKeyCompressLogs
                   withError:aError
                  withParams:aPath];
        }];
}

- (void)kickDevice:(NSDictionary *)param
    withMethodType:(NSString *)aChannelName
            result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    NSString *username = param[@"username"];
    NSString *password = param[@"password"];
    NSString *resource = param[@"resource"];
    Boolean isPassword = [param[@"isPassword"] boolValue];

    if (isPassword) {
        [AgoraChatClient.sharedClient
            kickDeviceWithUsername:username
                          password:password
                          resource:resource
                        completion:^(AgoraChatError *aError) {
                          [weakSelf onResult:result
                              withMethodType:ExtSdkMethodKeyKickDevice
                                   withError:aError
                                  withParams:nil];
                        }];
    } else {
        [AgoraChatClient.sharedClient
            kickDeviceWithUserId:username
                           token:password
                        resource:resource
                      completion:^(AgoraChatError *_Nullable aError) {
                        [weakSelf onResult:result
                            withMethodType:aChannelName
                                 withError:aError
                                withParams:nil];
                      }];
    }
}

- (void)kickAllDevices:(NSDictionary *)param
        withMethodType:(NSString *)aChannelName
                result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    NSString *username = param[@"username"];
    NSString *password = param[@"password"];
    Boolean isPassword = [param[@"isPassword"] boolValue];

    if (isPassword) {
        [AgoraChatClient.sharedClient
            kickAllDevicesWithUsername:username
                              password:password
                            completion:^(AgoraChatError *aError) {
                              [weakSelf onResult:result
                                  withMethodType:ExtSdkMethodKeyKickAllDevices
                                       withError:aError
                                      withParams:nil];
                            }];
    } else {
        [AgoraChatClient.sharedClient
            kickAllDevicesWithUserId:username
                               token:password
                          completion:^(AgoraChatError *_Nullable aError) {
                            [weakSelf onResult:result
                                withMethodType:aChannelName
                                     withError:aError
                                    withParams:nil];
                          }];
    }
}

- (void)isLoggedInBefore:(NSDictionary *)param
          withMethodType:(NSString *)aChannelName
                  result:(nonnull id<ExtSdkCallbackObjc>)result {
    [self onResult:result
        withMethodType:ExtSdkMethodKeyIsLoggedInBefore
             withError:nil
            withParams:@(AgoraChatClient.sharedClient.isLoggedIn)];
}

- (void)getLoggedInDevicesFromServer:(NSDictionary *)param
                      withMethodType:(NSString *)aChannelName
                              result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    NSString *username = param[@"username"];
    NSString *password = param[@"password"];
    Boolean isPassword = [param[@"isPassword"] boolValue];

    if (isPassword) {
        [AgoraChatClient.sharedClient
            getLoggedInDevicesFromServerWithUsername:username
                                            password:password
                                          completion:^(NSArray *aList,
                                                       AgoraChatError *aError) {
                                            NSMutableArray *list =
                                                [NSMutableArray array];
                                            for (AgoraChatDeviceConfig
                                                     *deviceInfo in aList) {
                                                [list addObject:
                                                          [deviceInfo
                                                              toJsonObject]];
                                            }

                                            [weakSelf onResult:result
                                                withMethodType:
                                                    ExtSdkMethodKeyGetLoggedInDevicesFromServer
                                                     withError:aError
                                                    withParams:aError ? nil
                                                                      : list];
                                          }];
    } else {
        [AgoraChatClient.sharedClient
            getLoggedInDevicesFromServerWithUserId:username
                                             token:password
                                        completion:^(
                                            NSArray<AgoraChatDeviceConfig *>
                                                *_Nullable aList,
                                            AgoraChatError *_Nullable aError) {
                                          NSMutableArray *list =
                                              [NSMutableArray array];
                                          for (AgoraChatDeviceConfig
                                                   *deviceInfo in aList) {
                                              [list
                                                  addObject:[deviceInfo
                                                                toJsonObject]];
                                          }
                                          [weakSelf onResult:result
                                              withMethodType:aChannelName
                                                   withError:aError
                                                  withParams:aError ? nil
                                                                    : list];
                                        }];
    }
}

- (void)loginWithAgoraToken:(NSDictionary *)param
             withMethodType:(NSString *)aChannelName
                     result:(nonnull id<ExtSdkCallbackObjc>)result {
    __weak typeof(self) weakSelf = self;
    NSString *username = param[@"username"];
    NSString *agoraToken = param[@"agoratoken"];
    [AgoraChatClient.sharedClient
        loginWithUsername:username
               agoraToken:agoraToken
               completion:^(NSString *aUsername, AgoraChatError *aError) {
                 [weakSelf onResult:result
                     withMethodType:ExtSdkMethodKeyLoginWithAgoraToken
                          withError:aError
                         withParams:@{
                             @"username" : aUsername,
                             @"token" : AgoraChatClient.sharedClient.accessUserToken
                         }];
               }];
}

- (void)isConnected:(NSDictionary *)param
     withMethodType:(NSString *)aChannelName
             result:(nonnull id<ExtSdkCallbackObjc>)result {
    [self onResult:result
        withMethodType:ExtSdkMethodKeyIsConnected
             withError:nil
            withParams:@(AgoraChatClient.sharedClient.isConnected)];
}

- (void)renewToken:(NSDictionary *)param
    withMethodType:(NSString *)aChannelName
            result:(nonnull id<ExtSdkCallbackObjc>)result {
    NSString *newAgoraToken = param[@"agora_token"];
    __weak typeof(self) weakSelf = self;
    [AgoraChatClient.sharedClient renewToken:newAgoraToken
                           completion:^(AgoraChatError *_Nullable aError) {
                             [weakSelf onResult:result
                                 withMethodType:ExtSdkMethodKeyRenewToken
                                      withError:aError
                                     withParams:nil];
                           }];
}

- (void)updatePushConfig:(NSDictionary *)param
          withMethodType:(NSString *)aChannelName
                  result:(nonnull id<ExtSdkCallbackObjc>)result {
    NSDictionary *dict = param[@"config"];
    NSString *deviceId = dict[@"deviceId"];
    NSString *deviceToken = dict[@"deviceToken"];
    //    NSData *deviceTokenData =
    //        [deviceToken dataUsingEncoding:NSUTF8StringEncoding];

    __weak typeof(self) weakSelf = self;
    [AgoraChatClient.sharedClient
        registerForRemoteNotificationsWithCertName:deviceId
                                       deviceToken:deviceToken
                                        completion:^(
                                            AgoraChatError *_Nullable aError) {
                                          [weakSelf onResult:result
                                              withMethodType:aChannelName
                                                   withError:aError
                                                  withParams:nil];
                                        }];
    //    AgoraChatError* error = [AgoraChatClient.sharedClient bindDeviceToken:[deviceToken
    //    dataUsingEncoding:NSUTF8StringEncoding]]; [self onResult:result
    //    withMethodType:aChannelName withError:error withParams:nil];

    //    [AgoraChatClient.sharedClient asyncBindDeviceToken:[deviceToken
    //    dataUsingEncoding:NSUTF8StringEncoding]] success:^{
    //        [self onResult:result withMethodType:aChannelName withError:nil
    //        withParams:nil];
    //    } failure:^(AgoraChatError *aError) {
    //        [self onResult:result withMethodType:aChannelName withError:aError
    //        withParams:nil];
    //    }];

    // must be NSString* type for deviceToken
    //    [AgoraChatClient.sharedClient
    //        registerForRemoteNotificationsWithDeviceToken:deviceToken
    //                                           completion:^(
    //                                               AgoraChatError *_Nullable aError)
    //                                               {
    //                                             [self onResult:result
    //                                                 withMethodType:aChannelName
    //                                                      withError:aError
    //                                                     withParams:nil];
    //                                           }];
}

- (void)activeNumbersReachLimitation {
    [self onReceive:ExtSdkMethodKeyOnAppActiveNumberReachLimit withParams:nil];
}

#pragma - mark AgoraChatClientDelegate

- (void)connectionStateDidChange:(AgoraChatConnectionState)aConnectionState {
    BOOL isConnected = aConnectionState == AgoraChatConnectionConnected;
    if (isConnected) {
        [self onReceive:ExtSdkMethodKeyOnConnected withParams:nil];
    } else {
        [self onReceive:ExtSdkMethodKeyOnDisconnected withParams:nil];
    }
}

- (void)autoLoginDidCompleteWithError:(AgoraChatError *)aError {
    if (aError.code == AgoraChatErrorServerServingForbidden) {
        [self userDidForbidByServer];
    } else if (aError.code == AgoraChatErrorAppActiveNumbersReachLimitation) {
        [self activeNumbersReachLimitation];
    }
}

- (void)tokenWillExpire:(AgoraChatErrorCode)aErrorCode {
    [self onReceive:ExtSdkMethodKeyOnTokenWillExpire withParams:nil];
}

- (void)tokenDidExpire:(AgoraChatErrorCode)aErrorCode {
    [self onReceive:ExtSdkMethodKeyOnTokenDidExpire withParams:nil];
}

- (void)onOfflineMessageSyncStart {
    [self onReceive:ExtSdkMethodKeyOnOfflineMessageSyncStart withParams:nil];
}

- (void)onOfflineMessageSyncFinish {
    [self onReceive:ExtSdkMethodKeyOnOfflineMessageSyncFinish withParams:nil];
}

- (void)userAccountDidLoginFromOtherDevice:(NSString *_Nullable)aDeviceName {
    [self onReceive:ExtSdkMethodKeyOnUserDidLoginFromOtherDevice
         withParams:@{@"deviceName" : aDeviceName}];
}

- (void)userAccountDidLoginFromOtherDeviceWithInfo:
    (AgoraChatLoginExtensionInfo *_Nullable)info {
    [self onReceive:ExtSdkMethodKeyOnUserDidLoginFromOtherDeviceWithInfo
         withParams:@{
             @"deviceName" : info.deviceName,
             @"ext" : info.extensionInfo
         }];
}

- (void)userAccountDidRemoveFromServer {
    [self onReceive:ExtSdkMethodKeyOnUserDidRemoveFromServer withParams:nil];
}

- (void)userDidForbidByServer {
    [self onReceive:ExtSdkMethodKeyOnUserDidForbidByServer withParams:nil];
}

- (void)userAccountDidForcedToLogout:(AgoraChatError *)aError {
    if (aError.code == AgoraChatErrorUserKickedByChangePassword) {
        [self onReceive:ExtSdkMethodKeyOnUserDidChangePassword withParams:nil];
    } else if (aError.code == AgoraChatErrorUserLoginTooManyDevices) {
        [self onReceive:ExtSdkMethodKeyOnUserDidLoginTooManyDevice
             withParams:nil];
    } else if (aError.code == AgoraChatErrorUserKickedByOtherDevice) {
        [self onReceive:ExtSdkMethodKeyOnUserKickedByOtherDevice
             withParams:nil];
    } else if (aError.code == AgoraChatErrorUserAuthenticationFailed) {
        [self onReceive:ExtSdkMethodKeyOnUserAuthenticationFailed
             withParams:nil];
    }
}

#pragma mark - AgoraChatMultiDevicesDelegate

- (void)multiDevicesContactEventDidReceive:(AgoraChatMultiDevicesEvent)aEvent
                                  username:(NSString *)aUsername
                                       ext:(NSString *)aExt {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"event"] = @(aEvent);
    data[@"target"] = aUsername;
    data[@"ext"] = aExt;
    data[@"type"] = ExtSdkMethodKeyOnMultiDeviceEventContact;
    [self onReceive:ExtSdkMethodKeyOnMultiDeviceEvent withParams:data];
}

- (void)multiDevicesGroupEventDidReceive:(AgoraChatMultiDevicesEvent)aEvent
                                 groupId:(NSString *)aGroupId
                                     ext:(id)aExt {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"event"] = @(aEvent);
    data[@"target"] = aGroupId;
    data[@"ext"] = aExt;
    data[@"type"] = ExtSdkMethodKeyOnMultiDeviceEventGroup;
    [self onReceive:ExtSdkMethodKeyOnMultiDeviceEvent withParams:data];
}

- (void)multiDevicesThreadEventDidReceive:(AgoraChatMultiDevicesEvent)aEvent
                                 threadId:(NSString *)aThreadId
                                      ext:(id)aExt {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"event"] = @(aEvent);
    data[@"target"] = aThreadId;
    data[@"ext"] = aExt;
    data[@"type"] = ExtSdkMethodKeyOnMultiDeviceEventThread;
    [self onReceive:ExtSdkMethodKeyOnMultiDeviceEvent withParams:data];
}

- (void)multiDevicesMessageBeRemoved:(NSString *_Nonnull)conversationId
                            deviceId:(NSString *_Nonnull)deviceId;
{
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"convId"] = conversationId;
    data[@"deviceId"] = deviceId;
    data[@"type"] = ExtSdkMethodKeyOnMultiDeviceEventRemoveMessage;
    [self onReceive:ExtSdkMethodKeyOnMultiDeviceEvent withParams:data];
}

- (void)multiDevicesConversationEvent:(AgoraChatMultiDevicesEvent)event
                       conversationId:(NSString *_Nonnull)conversationId
                     conversationType:(AgoraChatConversationType)conversationType {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"event"] = @(event);
    data[@"convId"] = conversationId;
    data[@"convType"] = @(conversationType);
    data[@"type"] = ExtSdkMethodKeyOnMultiDeviceEventConversation;
    [self onReceive:ExtSdkMethodKeyOnMultiDeviceEvent withParams:data];
}

@end
