#import <React/RCTConvert.h>
#import "RNZoomVideoSdk.h"
#import "RNZoomView.h"
#import "RNZoomCameraShareView.h"
#import "RCTConvert+RNZoomVideoSdk.h"
#import "RNZoomVideoSdkUser.h"
#import "RNZoomVideoSdkChatMessage.h"
#import "RNZoomVideoSdkLiveTranscriptionLanguage.h"
#import "RNZoomVideoSdkILiveTranscriptionMessageInfo.h"
#import "RNZoomVideoSdkAnnotationHelper.h"
#import "RNZoomVideoSdkShareAction.h"
#import "RNZoomVideoSdkSubSession.h"
#import <ZoomVideoSDK/ZoomVideoSDKRecordingHelper.h>
#import "SDKPiPHelper.h"
#import "SDKCallKitManager.h"

@implementation RNZoomVideoSdk

static ZoomVideoSDKRecordAgreementHandler* recordAgreementHandler;

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

-(NSArray<NSString *>*)supportedEvents
{
    return @[
            @"onSessionJoin",
            @"onSessionLeave",
            @"onUserJoin",
            @"onUserLeave",
            @"onUserVideoStatusChanged",
            @"onUserAudioStatusChanged",
            @"onUserShareStatusChanged",
            @"onLiveStreamStatusChanged",
            @"onChatNewMessageNotify",
            @"onUserNameChanged",
            @"onUserHostChanged",
            @"onUserManagerChanged",
            @"onUserActiveAudioChanged",
            @"onSessionNeedPassword",
            @"onSessionPasswordWrong",
            @"onError",
            @"getSdkVersion",
            @"isInSession",
            @"onCommandChannelConnectResult",
            @"onCommandReceived",
            @"onCloudRecordingStatus",
            @"onHostAskUnmute",
            @"onInviteByPhoneStatus",
            @"onChatDeleteMessageNotify",
            @"onLiveTranscriptionStatus",
            @"onLiveTranscriptionMsgInfoReceived",
            @"onLiveTranscriptionMsgError",
            @"onMultiCameraStreamStatusChanged",
            @"onRequireSystemPermission",
            @"onProxySettingNotification",
            @"onSSLCertVerifiedFailNotification",
            @"onUserVideoNetworkStatusChanged",
            @"onUserRecordingConsent",
            @"onCallCRCDeviceStatusChanged",
            @"onOriginalLanguageMsgReceived",
            @"onChatPrivilegeChanged",
            @"onAnnotationHelperCleanUp",
            @"onVideoCanvasSubscribeFail",
            @"onCameraControlRequestResult",
            @"onMicSpeakerVolumeChanged",
            @"onTestMicStatusChanged",
            @"onCalloutJoinSuccess",
            @"onShareCanvasSubscribeFail",
            @"onAnnotationPrivilegeChange",
            @"onShareContentSizeChanged",
            @"onShareContentChanged",
            @"onSubSessionStatusChanged",
            @"onSubSessionManagerHandle",
            @"onSubSessionParticipantHandle",
            @"onSubSessionUsersUpdate",
            @"onSubSessionUserHelpRequest",
            @"onSubSessionUserHelpRequestResult",
            @"onBroadcastMessageFromMainSession",
            @"onUserWhiteboardShareStatusChanged",
            @"onWhiteboardExported",
            @"onStartBroadcastResponse",
            @"onStopBroadcastResponse",
            @"onGetBroadcastControlStatus",
            @"onStreamingJoinStatusChanged",
    ];
}

RCT_EXPORT_MODULE()

RCT_REMAP_METHOD(initSdk,
        initSdkWithConfig: (NSDictionary *)config
        withResolver:(RCTPromiseResolveBlock)resolve
        withRejecter:(RCTPromiseRejectBlock)reject)
{
    ZoomVideoSDKInitParams *initParams = [[ZoomVideoSDKInitParams alloc] init];

    initParams.domain = [config valueForKey:@"domain"];
    initParams.enableLog = [[config objectForKey:@"enableLog"] boolValue];
    initParams.logFilePrefix = [config valueForKey:@"logFilePrefix"];
    initParams.appGroupId = [config valueForKey:@"appGroupId"];

    if ([config objectForKey:@"videoRawDataMemoryMode"]) {
        initParams.videoRawdataMemoryMode = [RCTConvert ZoomVideoSDKRawDataMemoryMode: [config objectForKey:@"videoRawDataMemoryMode"]];
    }
    if ([config objectForKey:@"audioRawDataMemoryMode"]) {
        initParams.audioRawdataMemoryMode = [RCTConvert ZoomVideoSDKRawDataMemoryMode: [config objectForKey:@"audioRawDataMemoryMode"]];
    }
    if ([config objectForKey:@"shareRawDataMemoryMode"]) {
        initParams.shareRawdataMemoryMode = [RCTConvert ZoomVideoSDKRawDataMemoryMode: [config objectForKey:@"shareRawDataMemoryMode"]];
    }
    if ([config objectForKey:@"enableCallKit"]) {
        [[SDKCallKitManager sharedManager] setEnableCallKit:[[config objectForKey:@"enableCallKit"] boolValue]];
    }
    NSString *speakerFilePath = [config valueForKey:@"speakerFilePath"];
    ZoomVideoSDKExtendParams *extendParams = [[ZoomVideoSDKExtendParams alloc] init];
    if (speakerFilePath.length != 0) {
        extendParams.speakerTestFilePath = speakerFilePath;
    }
    extendParams.wrapperType = 2;
    initParams.extendParam = extendParams;

    dispatch_async(dispatch_get_main_queue(), ^{
        ZoomVideoSDKError ret = [[ZoomVideoSDK shareInstance] initialize:initParams];

        switch (ret) {
            case Errors_Success:
                NSLog(@"SDK initialized successfully");
                // Register delegate immediately on init so that ALL events
                // (session, broadcast streaming viewer, etc.) reach JS
                // without depending on a session being joined.
                [ZoomVideoSDK shareInstance].delegate = self;
                resolve(@"SDK initialized successfully");
                break;
            default:
                NSLog(@"SDK failed to initialize with error code: %lu", (unsigned long)ret);
                reject(@"sdkinit_failed", [[RCTConvert ZoomVideoSDKErrorValuesReversed] objectForKey: @(ret)], nil);
                break;
        }
    });

    // Setup My Video Rotation. NOTE: We may eventually want to make this configurable.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDeviceOrientationChangeNotification:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

RCT_REMAP_METHOD(joinSession,
        joinSessionWithConfig: (NSDictionary *)config
        withResolver: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    NSString* token = [config valueForKey:@"token"];
    NSString* sessionName = [config valueForKey:@"sessionName"];
    NSString* sessionPassword = [config valueForKey:@"sessionPassword"];
    NSString* userName = [config valueForKey:@"userName"];
    NSInteger* sessionIdleTimeoutMins = [[config valueForKey:@"sessionIdleTimeoutMins"] intValue];

    ZoomVideoSDKAudioOptions *audioOption = [ZoomVideoSDKAudioOptions new];
    NSDictionary* audioOptionConfig = [config valueForKey:@"audioOptions"];
    if (audioOptionConfig != nil) {
        audioOption.connect     = [[audioOptionConfig valueForKey:@"connect"] boolValue];
        audioOption.mute        = [[audioOptionConfig valueForKey:@"mute"] boolValue];
        audioOption.autoAdjustSpeakerVolume = [[audioOptionConfig valueForKey:@"autoAdjustSpeakerVolume"] boolValue];
    }
    ZoomVideoSDKVideoOptions *videoOption = [ZoomVideoSDKVideoOptions new];
    NSDictionary* videoOptionConfig = [config valueForKey:@"videoOptions"];
    if (videoOptionConfig != nil) {
        videoOption.localVideoOn = [[videoOptionConfig valueForKey:@"localVideoOn"] boolValue];
    }

    ZoomVideoSDKSessionContext *sessionContext = [[ZoomVideoSDKSessionContext alloc] init];
    // Ensure that you do not hard code JWT or any other confidential credentials in your production app.
    sessionContext.token = token;
    sessionContext.sessionName = sessionName;
    sessionContext.sessionPassword = sessionPassword;
    sessionContext.userName = userName;
    sessionContext.audioOption = audioOption;
    sessionContext.videoOption = videoOption;
    sessionContext.sessionIdleTimeoutMins = sessionIdleTimeoutMins;

    // Delegate is registered on initSdk; no need to re-set here.

    dispatch_async(dispatch_get_main_queue(), ^{
        ZoomVideoSDKSession *session = [[ZoomVideoSDK shareInstance] joinSession:sessionContext];

        if (session) {
            // Session joined successfully.
            resolve(nil);
        } else {
            reject(@"joinSession_failure", @"Join Session failed", nil);
        }
    });
}

RCT_EXPORT_METHOD(leaveSession: (BOOL)shouldEndSession
        withResolver: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        resolve([[RCTConvert ZoomVideoSDKErrorValuesReversed] objectForKey: @([[ZoomVideoSDK shareInstance] leaveSession: shouldEndSession])]);
    });
}

RCT_EXPORT_METHOD(getSdkVersion: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    resolve([[ZoomVideoSDK shareInstance] getSDKVersion]);
}

RCT_EXPORT_METHOD(isInSession: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    resolve(@([[ZoomVideoSDK shareInstance] isInSession]));
}

RCT_EXPORT_METHOD(cleanup: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        ZoomVideoSDKError ret = [[ZoomVideoSDK shareInstance] cleanup];
        switch (ret) {
            case Errors_Success:
                NSLog(@"SDK cleanup successfully");
                break;
            default:
                NSLog(@"SDK failed to cleanup with error code: %lu", (unsigned long)ret);
                break;
        }
    });
}

RCT_EXPORT_METHOD(acceptRecordingConsent: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    if (recordAgreementHandler != NULL) {
        resolve(@([recordAgreementHandler accept]));
    } else {
        resolve(@FALSE);
    }
}

RCT_EXPORT_METHOD(declineRecordingConsent: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    if (recordAgreementHandler != NULL) {
        resolve(@([recordAgreementHandler decline]));
    } else {
        resolve(@FALSE);
    }
}

RCT_EXPORT_METHOD(getRecordingConsentType: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    if (recordAgreementHandler != NULL) {
        resolve([[RCTConvert ZoomVideoSDKRecordAgreementTypeValuesReversed] objectForKey: @(recordAgreementHandler.agreementType)]);
    } else {
        resolve(@"ConsentType_Invalid");
    }
}

RCT_EXPORT_METHOD(exportLog: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        resolve([[ZoomVideoSDK shareInstance] exportLog]);
    });
}

RCT_EXPORT_METHOD(cleanAllExportedLogs: (RCTPromiseResolveBlock)resolve
        withRejecter: (RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        resolve([[RCTConvert ZoomVideoSDKErrorValuesReversed] objectForKey: @([[ZoomVideoSDK shareInstance] cleanAllExportedLogs])]);
    });
}

- (void) onError:(ZoomVideoSDKError)ErrorType detail:(NSInteger)details {
    switch (ErrorType) {
        case Errors_Success:
            // Your ZoomVideoSDK operation was successful.
            NSLog(@"Success");
            break;
        default:
            // Your ZoomVideoSDK operation raised an error.
            // Refer to error code documentation.
            NSLog(@"Error %lu %ld", (unsigned long)ErrorType, (long)details);
            break;
    }

    [self sendEventWithName:@"onError" body:@{ @"errorType": [[RCTConvert ZoomVideoSDKErrorValuesReversed] objectForKey: @(ErrorType)], @"details": @(details) }];
}

- (void)onSessionJoin {
    [self sendEventWithName:@"onSessionJoin" body: @{ @"mySelf": [RNZoomVideoSdkUser mapUser: [[[ZoomVideoSDK shareInstance] getSession] getMySelf]] }];

    // Set initial video orientation
    [self onDeviceOrientationChangeNotification:nil];
}

- (void)onSessionLeave:(ZoomVideoSDKSessionLeaveReason)reason {
    [[SDKPiPHelper shared] cleanUpPictureInPicture];
    [[SDKCallKitManager sharedManager] endCallWithComplete:^{
        NSLog(@"----CallKitManager endCall Complete ------");
    }];
    [self sendEventWithName:@"onSessionLeave" body:@{ @"reason": [[RCTConvert ZoomVideoSDKSessionLeaveReasonValuesReversed] objectForKey: @(reason)]}];
}

- (void)onUserJoin:(ZoomVideoSDKUserHelper *)helper users:(NSArray<ZoomVideoSDKUser *> *)userArray {
    [self sendEventWithName:@"onUserJoin" body: @{@"joinedUsers": [RNZoomVideoSdkUser mapUserArray:userArray], @"remoteUsers": [RNZoomVideoSdkUser mapUserArray: [[[ZoomVideoSDK shareInstance] getSession] getRemoteUsers]] }];
}

- (void)onUserLeave:(ZoomVideoSDKUserHelper *)helper users:(NSArray<ZoomVideoSDKUser *> *)userArray {
    NSArray<ZoomVideoSDKUser *> *remoteUsers =
            [[[ZoomVideoSDK shareInstance] getSession] getRemoteUsers];
    NSMutableArray<ZoomVideoSDKUser *> *filteredUsers =
            [remoteUsers mutableCopy];
    for (ZoomVideoSDKUser *leavingUser in userArray) {
        NSInteger leavingId = [leavingUser getUserID];

        for (ZoomVideoSDKUser *user in remoteUsers) {
            if ([user getUserID] == leavingId) {
                [filteredUsers removeObject:user];
                break;
            }
        }
    }

    [self sendEventWithName:@"onUserLeave" body: @{@"leftUsers": [RNZoomVideoSdkUser mapUserArray:userArray], @"remoteUsers": [RNZoomVideoSdkUser mapUserArray: filteredUsers] }];
}

- (void)onUserVideoStatusChanged:(ZoomVideoSDKVideoHelper *)helper user:(NSArray<ZoomVideoSDKUser *> *)userArray {
    [self sendEventWithName:@"onUserVideoStatusChanged" body:@{@"changedUsers": [RNZoomVideoSdkUser mapUserArray: userArray]}];
}

- (void)onUserAudioStatusChanged:(ZoomVideoSDKAudioHelper *)helper user:(NSArray<ZoomVideoSDKUser *> *)userArray {
    [self sendEventWithName:@"onUserAudioStatusChanged" body:@{@"changedUsers": [RNZoomVideoSdkUser mapUserArray: userArray]}];
}

- (void)onUserShareStatusChanged:(ZoomVideoSDKShareHelper * _Nullable)helper user:(ZoomVideoSDKUser * _Nullable)user shareAction:(ZoomVideoSDKShareAction*_Nullable)shareAction {
    [self sendEventWithName:@"onUserShareStatusChanged" body:@{
            @"user": [RNZoomVideoSdkUser mapUser: user],
            @"shareAction": [RNZoomVideoSdkShareAction mapShareAction: shareAction],
    }];
}

- (void)onLiveStreamStatusChanged:(ZoomVideoSDKLiveStreamHelper *)helper status:(ZoomVideoSDKLiveStreamStatus)status {
    [self sendEventWithName:@"onLiveStreamStatusChanged" body:@{
            @"status": [[RCTConvert ZoomVideoSDKLiveStreamStatusValuesReversed ] objectForKey: @(status)]
    }];
}

- (void)onChatNewMessageNotify:(ZoomVideoSDKChatHelper *)helper message:(ZoomVideoSDKChatMessage *)chatMessage {
    [self sendEventWithName:@"onChatNewMessageNotify" body:[RNZoomVideoSdkChatMessage mapChatMessage:chatMessage]];
}

- (void)onUserNameChanged:(ZoomVideoSDKUser *)user {
    [self sendEventWithName:@"onUserNameChanged" body:@{@"changedUser": [RNZoomVideoSdkUser mapUser: user]}];
}

- (void)onUserHostChanged:(ZoomVideoSDKUserHelper *)helper user:(ZoomVideoSDKUser *)user {
    [self sendEventWithName:@"onUserHostChanged" body:@{@"changedUser": [RNZoomVideoSdkUser mapUser: user]}];
}

- (void)onUserManagerChanged:(ZoomVideoSDKUser *)user {
    [self sendEventWithName:@"onUserManagerChanged" body:@{@"changedUser": [RNZoomVideoSdkUser mapUser: user]}];
}

- (void)onUserActiveAudioChanged:(ZoomVideoSDKUserHelper *)helper users:(NSArray<ZoomVideoSDKUser *> *)userArray {
    [self sendEventWithName:@"onUserActiveAudioChanged" body:@{@"changedUsers": [RNZoomVideoSdkUser mapUserArray: userArray]}];
}

- (void)onSessionNeedPassword:(ZoomVideoSDKError (^)(NSString *, BOOL))completion {
    NSString *userInput = NULL;
    Boolean cancelJoinSession = YES;
    if (completion) {
        completion(userInput, cancelJoinSession);
    }

    [self sendEventWithName:@"onSessionNeedPassword" body:nil];
}

- (void)onSessionPasswordWrong:(ZoomVideoSDKError (^)(NSString *, BOOL))completion {
    NSString *userInput = NULL;
    Boolean cancelJoinSession = YES;
    if (completion) {
        completion(userInput, cancelJoinSession);
    }

    [self sendEventWithName:@"onSessionPasswordWrong" body:nil];
}

- (void)onCmdChannelConnectResult:(BOOL)success {
    [self sendEventWithName:@"onCommandChannelConnectResult" body:@{@"success": [NSNumber numberWithBool:success]}];
}

- (void)onCommandReceived:(NSString * _Nullable)commandContent sendUser:(ZoomVideoSDKUser * _Nullable)sendUser {
    [self sendEventWithName:@"onCommandReceived" body:@{@"sender": [NSNumber numberWithInteger:[sendUser getUserID]], @"command": commandContent}];
}

- (void)onCloudRecordingStatus:(ZoomVideoSDKRecordingStatus)status recordAgreementHandler:(ZoomVideoSDKRecordAgreementHandler * _Nullable)handler {
    if (handler != NULL) {
        recordAgreementHandler = handler;
    }
    NSDictionary* statusDic = [[RCTConvert ZoomVideoSDKRecordingStatusValuesReversed] objectForKey: @(status)];
    if (statusDic != nil) {
        [self sendEventWithName:@"onCloudRecordingStatus" body:@{
                @"status": statusDic
        }];
    }
}

- (void)onUserRecordAgreementNotification:(ZoomVideoSDKUser * _Nullable)user {
    if (user != NULL) {
        [self sendEventWithName:@"onUserRecordingConsent" body: @{
                @"user": [RNZoomVideoSdkUser mapUser: user]
        }];
    }
}

- (void)onHostAskUnmute {
    [self sendEventWithName:@"onHostAskUnmute" body: nil];
}

- (void)onInviteByPhoneStatus:(ZoomVideoSDKPhoneStatus)status failReason:(ZoomVideoSDKPhoneFailedReason)reason {
    [self sendEventWithName:@"onInviteByPhoneStatus" body:@{
            @"status": [[RCTConvert ZoomVideoSDKPhoneStatusValuesReversed] objectForKey: @(status)],
            @"reason": [[RCTConvert ZoomVideoSDKPhoneFailedReasonValuesReversed] objectForKey: @(reason)]
    }];
}

- (void)onMultiCameraStreamStatusChanged:(ZoomVideoSDKMultiCameraStreamStatus)status parentUser:(ZoomVideoSDKUser *_Nullable)user videoPipe:(ZoomVideoSDKRawDataPipe *_Nullable)videoPipe {

}

- (void)onMultiCameraStreamStatusChanged:(ZoomVideoSDKMultiCameraStreamStatus)status parentUser:(ZoomVideoSDKUser *_Nullable)user videoCanvas:(ZoomVideoSDKVideoCanvas *_Nullable)videoCanvas {
    [self sendEventWithName:@"onMultiCameraStreamStatusChanged" body:@{
            @"status": [[RCTConvert ZoomVideoSDKMultiCameraStreamStatusValuesReversed] objectForKey: @(status)],
            @"user": [RNZoomVideoSdkUser mapUser: user]
    }];
}

- (void)onChatMsgDeleteNotification:(ZoomVideoSDKChatHelper * _Nullable)helper messageID:(NSString * __nonnull)msgID deleteBy:(ZoomVideoSDKChatMsgDeleteBy) type {
    [self sendEventWithName:@"onChatDeleteMessageNotify" body:@{
            @"msgID": msgID,
            @"type": [[RCTConvert ZoomVideoSDKChatMsgDeleteByValuesReversed] objectForKey: @(type)]
    }];
}

- (void)onLiveTranscriptionStatus:(ZoomVideoSDKLiveTranscriptionStatus)status {
    [self sendEventWithName:@"onLiveTranscriptionStatus" body:@{
            @"status": [[RCTConvert ZoomVideoSDKLiveTranscriptionStatusValuesReversed] objectForKey: @(status)]
    }];
}

- (void)onOriginalLanguageMsgReceived:(ZoomVideoSDKLiveTranscriptionMessageInfo *_Nullable)messageInfo {
    [self sendEventWithName:@"onOriginalLanguageMsgReceived" body:@{
            @"messageInfo": [RNZoomVideoSdkILiveTranscriptionMessageInfo mapMessageInfo: messageInfo]
    }];
}

- (void)onLiveTranscriptionMsgError:(ZoomVideoSDKLiveTranscriptionLanguage *)spokenLanguage transLanguage:(ZoomVideoSDKLiveTranscriptionLanguage *)transcriptLanguage {
    [self sendEventWithName:@"onLiveTranscriptionMsgError" body:@{
            @"spokenLanguage": [RNZoomVideoSdkLiveTranscriptionLanguage mapLanguage: spokenLanguage],
            @"transcriptLanguage": [RNZoomVideoSdkLiveTranscriptionLanguage mapLanguage: transcriptLanguage]
    }];
}

- (void)onRequireSystemPermission:(ZoomVideoSDKSystemPermissionType)permissionType {
    [self sendEventWithName:@"onRequireSystemPermission" body:@{
            @"permissionType": [[RCTConvert ZoomVideoSDKSystemPermissionTypeValuesReversed] objectForKey: @(permissionType)]
    }];
}

- (void)onChatPrivilegeChanged:(ZoomVideoSDKChatHelper * _Nullable)helper privilege:(ZoomVideoSDKChatPrivilegeType)currentPrivilege {
    [self sendEventWithName:@"onChatPrivilegeChanged" body: @{
            @"privilege": [[RCTConvert ZoomVideoSDKChatPrivilegeTypeValuesReversed] objectForKey: @(currentPrivilege)],
    }];
}

- (void)onAnnotationHelperCleanUp:(ZoomVideoSDKAnnotationHelper *)helper
{
    [[RNZoomVideoSdkAnnotationHelper alloc] setAnnotationHelper:nil];
    [[[ZoomVideoSDK shareInstance] getShareHelper] destroyAnnotationHelper:helper];
    [self sendEventWithName:@"onAnnotationHelperCleanUp" body: nil];
}

// OS Event Listeners

- (void)onDeviceOrientationChangeNotification:(NSNotification *)aNotification {
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    if ((orientation == UIDeviceOrientationUnknown) || (orientation == UIDeviceOrientationFaceUp) || (orientation == UIDeviceOrientationFaceDown))
    {
        orientation = (UIDeviceOrientation)[[UIApplication sharedApplication] statusBarOrientation];
    }

    dispatch_async(dispatch_get_main_queue(), ^{
        [[[ZoomVideoSDK shareInstance] getVideoHelper] rotateMyVideo:orientation];
    });
}

- (void)onProxySettingNotification:(ZoomVideoSDKProxySettingHandler *_Nonnull)handler {
    [self sendEventWithName:@"onProxySettingNotification" body: @{
            @"proxyHost": [handler proxyHost],
            @"proxyPort": [NSNumber numberWithInteger:[handler proxyPort]],
            @"proxyDescription": [handler proxyDescription]
    }];
}

- (void)onSSLCertVerifiedFailNotification:(ZoomVideoSDKSSLCertificateInfo *_Nonnull)handler {
    [self sendEventWithName:@"onSSLCertVerifiedFailNotification" body: @{
            @"certIssuedTo": [handler certIssuedTo],
            @"certIssuedBy": [handler certIssuedBy],
            @"certSerialNum": [handler certSerialNum],
            @"certFingerprint": [handler certFingerprint]
    }];
}

- (void)onUserVideoNetworkStatusChanged:(ZoomVideoSDKNetworkStatus)status user:(ZoomVideoSDKUser *)user {
    [self sendEventWithName:@"onUserVideoNetworkStatusChanged" body: @{
            @"status": [[RCTConvert ZoomVideoSDKNetworkStatusValuesReversed] objectForKey: @(status)],
            @"user": [RNZoomVideoSdkUser mapUser: user],
    }];
}

- (void)onCallCRCDeviceStatusChanged:(ZoomVideoSDKCRCCallStatus)status {
    [self sendEventWithName:@"onCallCRCDeviceStatusChanged" body: @{
            @"status": [[RCTConvert ZoomVideoSDKCRCCallStatusValuesReversed] objectForKey: @(status)],
    }];
}

- (void)onVideoCanvasSubscribeFail:(ZoomVideoSDKSubscribeFailReason)failReason user:(ZoomVideoSDKUser *_Nullable)user view:(UIView *_Nullable)view
{
    [self sendEventWithName:@"onVideoCanvasSubscribeFail" body: @{
            @"failReason": [[RCTConvert ZoomVideoSDKSubscribeFailReasonValuesReversed] objectForKey: @(failReason)],
            @"user": [RNZoomVideoSdkUser mapUser: user],
    }];
}

- (void)onCameraControlRequestResult:(ZoomVideoSDKUser*)user approved:(BOOL)isApproved
{
    [self sendEventWithName:@"onCameraControlRequestResult" body: @{
            @"approved": @(isApproved),
            @"user": [RNZoomVideoSdkUser mapUser: user],
    }];
}

- (void)onMicSpeakerVolumeChanged:(int)micVolume speakerVolume:(int)speakerVolume
{
    [self sendEventWithName:@"onMicSpeakerVolumeChanged" body: @{
            @"micVolume": [NSNumber numberWithInt:micVolume],
            @"speakerVolume": [NSNumber numberWithInt:speakerVolume],
    }];
}

- (void)onTestMicStatusChanged:(ZoomVideoSDKTestMicStatus)status
{
    [self sendEventWithName:@"onTestMicStatusChanged" body: @{
            @"status": [[RCTConvert ZoomVideoSDKTestMicStatusValuesReversed] objectForKey: @(status)],
    }];
}

- (void)onCalloutJoinSuccess:(ZoomVideoSDKUser * _Nullable)user phoneNumber:(NSString * _Nullable)phoneNumber
{
    [self sendEventWithName:@"onCalloutJoinSuccess" body:@{
            @"phoneNumber": phoneNumber,
            @"user": [RNZoomVideoSdkUser mapUser: user]
    }];
}

- (void)onAnnotationPrivilegeChangeWithUser:(ZoomVideoSDKUser *_Nullable)user shareAction:(ZoomVideoSDKShareAction*_Nullable)shareAction {
    [self sendEventWithName:@"onAnnotationPrivilegeChange" body:@{
            @"shareAction": [RNZoomVideoSdkShareAction mapShareAction: shareAction],
            @"shareOwner": [RNZoomVideoSdkUser mapUser: user],
    }];
}

- (void)onShareCanvasSubscribeFailWithUser:(ZoomVideoSDKUser *_Nullable)user view:(UIView *_Nullable)view shareAction:(ZoomVideoSDKShareAction*_Nullable)shareAction {
    [self sendEventWithName:@"onShareCanvasSubscribeFail" body:@{
            @"shareAction": [RNZoomVideoSdkShareAction mapShareAction: shareAction],
            @"user": [RNZoomVideoSdkUser mapUser: user],
    }];
}

- (void)onShareContentChanged:(ZoomVideoSDKShareHelper *_Nullable)shareHelper user:(ZoomVideoSDKUser *_Nullable)user shareAction:(ZoomVideoSDKShareAction *_Nullable)shareAction {
    [self sendEventWithName:@"onShareContentChanged" body:@{
            @"shareAction": [RNZoomVideoSdkShareAction mapShareAction: shareAction],
            @"user": [RNZoomVideoSdkUser mapUser: user],
    }];
}

- (void)onShareContentSizeChanged:(ZoomVideoSDKShareHelper * _Nullable)helper user:(ZoomVideoSDKUser * _Nullable)user shareAction:(ZoomVideoSDKShareAction*_Nullable)shareAction {
    [self sendEventWithName:@"onShareContentSizeChanged" body:@{
            @"shareAction": [RNZoomVideoSdkShareAction mapShareAction: shareAction],
            @"user": [RNZoomVideoSdkUser mapUser: user],
    }];
}

- (void)onSubSessionStatusChanged:(ZoomVideoSDKSubSessionStatus)status subSession:(NSArray<ZoomVideoSDKSubSessionKit *> *)pSubSessionKitList
{
    [self sendEventWithName:@"onSubSessionStatusChanged" body:@{
            @"status": [[RCTConvert ZoomVideoSDKSubSessionStatusValuesReversed] objectForKey:@(status)],
            @"subSessionKitList": [RNZoomVideoSdkSubSession mapSubSessionKitArray:pSubSessionKitList]
    }];
}

- (void)onSubSessionManagerHandle:(ZoomVideoSDKSubSessionManager *)pManager
{
    [RNZoomVideoSdkSubSession storeSubSessionManager:pManager];
    [self sendEventWithName:@"onSubSessionManagerHandle" body:@{}];
}

- (void)onSubSessionParticipantHandle:(ZoomVideoSDKSubSessionParticipant *)pParticipant
{
    [RNZoomVideoSdkSubSession storeSubSessionParticipant:pParticipant];
    [self sendEventWithName:@"onSubSessionParticipantHandle" body:@{}];
}

- (void)onSubSessionUsersUpdate:(ZoomVideoSDKSubSessionKit *)pSubSessionKit
{
    [self sendEventWithName:@"onSubSessionUsersUpdate" body:@{
            @"subSessionKit": [RNZoomVideoSdkSubSession mapSubSessionKit:pSubSessionKit]
    }];
}

- (void)onSubSessionUserHelpRequestHandler:(ZoomVideoSDKSubSessionUserHelpRequestHandler *)pHandler
{
    [RNZoomVideoSdkSubSession storeSubSessionUserHelpRequestHandler:pHandler];
    [self sendEventWithName:@"onSubSessionUserHelpRequest" body:@{}];
}

- (void)onSubSessionUserHelpRequestResult:(ZoomVideoSDKUserHelpRequestResult)result
{
    [self sendEventWithName:@"onSubSessionUserHelpRequestResult" body:@{
            @"status": [[RCTConvert ZoomVideoSDKUserHelpRequestResultValuesReversed] objectForKey:@(result)]
    }];
}

- (void)onBroadcastMessage:(NSString *)message userName:(NSString *)userName
{
    [self sendEventWithName:@"onBroadcastMessageFromMainSession" body:@{
            @"message": message ?: @"",
            @"userName": userName ?: @""
    }];
}

-(void)onUserWhiteboardShareStatusChanged:(ZoomVideoSDKUser *_Nonnull)user whiteboardhelper:(ZoomVideoSDKWhiteboardHelper*_Nonnull)whiteboardHelper {
    [self sendEventWithName:@"onUserWhiteboardShareStatusChanged" body:@{
            @"user": [RNZoomVideoSdkUser mapUser:user]
    }];
}

- (void)onWhiteboardExported:(ZoomVideoSDKWhiteboardExportFormatType)format data:(NSData*)data {
    NSString* formatString = [[RCTConvert ZoomVideoSDKWhiteboardExportFormatTypeValuesReversed] objectForKey: @(format)];
    NSString* filePath = nil;
    NSString* error = nil;

    if (data == nil || data.length == 0) {
        error = @"No data to save";
    } else {
        @try {
            // Determine file extension based on format
            NSString* fileExtension = @".pdf";
            if (format == ZoomVideoSDKWhiteboardExport_Format_PDF) {
                fileExtension = @".pdf";
            }

            // Generate unique filename with timestamp
            NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
            [formatter setDateFormat:@"yyyyMMdd_HHmmss"];
            NSString* timestamp = [formatter stringFromDate:[NSDate date]];
            NSString* fileName = [NSString stringWithFormat:@"whiteboard_export_%@%@", timestamp, fileExtension];

            // Get documents directory
            NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString* documentsDirectory = [paths objectAtIndex:0];
            NSString* filePathFull = [documentsDirectory stringByAppendingPathComponent:fileName];

            // Save file
            BOOL success = [data writeToFile:filePathFull atomically:YES];
            if (success) {
                filePath = filePathFull;
            } else {
                error = @"Failed to save file";
            }
        } @catch (NSException *e) {
            error = [NSString stringWithFormat:@"Failed to save file: %@", e.reason];
        }
    }

    NSMutableDictionary* body = [NSMutableDictionary dictionaryWithObject:formatString forKey:@"format"];
    if (filePath) {
        [body setObject:filePath forKey:@"filePath"];
    }
    if (error) {
        [body setObject:error forKey:@"error"];
    }

    [self sendEventWithName:@"onWhiteboardExported" body:body];
}

#pragma mark - Broadcast Streaming

- (void)onStartBroadcastResponse:(BOOL)isSuccess channelID:(NSString *)channelID
{
    [self sendEventWithName:@"onStartBroadcastResponse" body:@{
            @"isSuccess": @(isSuccess),
            @"channelID": channelID ?: @""
    }];
}

- (void)onStopBroadcastResponse:(BOOL)isSuccess
{
    [self sendEventWithName:@"onStopBroadcastResponse" body:@{
            @"isSuccess": @(isSuccess)
    }];
}

- (void)onGetBroadcastControlStatus:(BOOL)isSuccess status:(ZoomVideoSDKBroadcastControlStatus)status
{
    NSString *statusString = [[RCTConvert ZoomVideoSDKBroadcastControlStatusValuesReversed] objectForKey:@(status)];
    [self sendEventWithName:@"onGetBroadcastControlStatus" body:@{
            @"isSuccess": @(isSuccess),
            @"status": statusString ?: @""
    }];
}

- (void)onStreamingJoinStatusChanged:(ZoomVideoSDKStreamingJoinStatus)status
{
    NSString *statusString = [[RCTConvert ZoomVideoSDKStreamingJoinStatusValuesReversed] objectForKey:@(status)];
    [self sendEventWithName:@"onStreamingJoinStatusChanged" body:@{
            @"status": statusString ?: @""
    }];
}

@end
