#import <React/RCTViewManager.h>
#import <React/RCTUIManager.h>
#import <React/RCTLog.h>
#import "RCTPlayspacePlayer.h"
#import "CNConnatixPlayer+CNEventTypeMapper.h"
#import "connatix_player_sdk_react_native-Swift.h"

@interface PlayspacePlayerViewManager : RCTViewManager
@end

@implementation PlayspacePlayerViewManager

const NSString* ERROR_KEY_PLAYSPACE = @"error";
const NSString* RESULT_KEY_PLAYSPACE = @"result";

RCT_EXPORT_MODULE(RCTPlayspacePlayer)

RCT_EXPORT_VIEW_PROPERTY(onSetPlayspaceConfig, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(onPlay, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPause, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetVolume, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetQuality, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetQuality, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onDisableAdvertising, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onEnableAdvertising, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetMacros, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetPreRollBreak, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetPostRollBreak, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(onGetStoryPosition, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetStoryPosition, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetStoryId, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetStoryTimeline, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetLayout, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetLayout, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetCtaLabel, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetStoryMetadata, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(onListenFor, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onListenForMore, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onRemove, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPlayerEvent, RCTBubblingEventBlock)

- (UIView *)view {
    return [[RCTPlayspacePlayer alloc] initWithFrame:CGRectZero];
}

- (RCTPlayspacePlayer *)getPlayerView:(NSNumber *) reactTag {
    if (self.bridge == nil) {
        return nil;
    }

    RCTPlayspacePlayer * view = (RCTPlayspacePlayer *)[self.bridge.uiManager viewForReactTag:reactTag];
    if (view != nil) {
        return view;
    }

    NSDictionary<NSNumber *, UIView *>* viewRegistry = [self.bridge.uiManager valueForKey:@"viewRegistry"];
    if (viewRegistry != nil) {
        RCTPlayspacePlayer * viewRegistryView = (RCTPlayspacePlayer *)viewRegistry[reactTag];
        if (viewRegistryView != nil) {
            return viewRegistryView;
        } else {
            return nil;
        }
    } else {
        return nil;
    }
}

- (UIView *)getObstructionView:(NSNumber *) reactTag {
    if (self.bridge == nil) {
        return nil;
    }

    UIView * view = (UIView *)[self.bridge.uiManager viewForReactTag:reactTag];
    if (view != nil) {
        return view;
    }

    NSDictionary<NSNumber *, UIView *>* viewRegistry = [self.bridge.uiManager valueForKey:@"viewRegistry"];
    if (viewRegistry != nil) {
        UIView * viewRegistryView = (UIView *)viewRegistry[reactTag];
        if (viewRegistryView != nil) {
            return viewRegistryView;
        } else {
            return nil;
        }
    } else {
        return nil;
    }
}

RCT_EXPORT_METHOD(setPlayspaceConfig:(nonnull NSNumber *) reactTag :(NSString *)config) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        NSData *data = [config dataUsingEncoding:NSUTF8StringEncoding];
        NSError *error = nil;
        CNPlayspaceConfig* playerConfig = [ObjcHelper decodeDataToPlayspaceConfigWithData:data error:&error];
        playerConfig._appSettings.reactNativeSdkVersion = @"2.4.10";
        if (error) {
            view.onSetPlayspaceConfig(@{ERROR_KEY_PLAYSPACE: error.userInfo[NSDebugDescriptionErrorKey]});
        } else {
            [view.playspacePlayer setPlayspaceConfig:playerConfig];
            view.onSetPlayspaceConfig(@{RESULT_KEY_PLAYSPACE: @YES});
        }
    }];
}

RCT_EXPORT_METHOD(stopPlayer:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        [view.playspacePlayer stopPlayer];
    }];
}

// Base API

RCT_EXPORT_METHOD(play:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer playOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onPlay(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(pause:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer pauseOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onPause(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setVolume:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)volume) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setVolume:volume.floatValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetVolume(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setQuality:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)videoQuality) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setQuality:videoQuality.intValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetQuality(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getQuality:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer getQualityOnResult:^(enum CNVideoQuality videoQuality) {
            NSNumber* result = [ObjcHelper getVideoQualityNumber:videoQuality];
            weakView.onGetQuality(@{RESULT_KEY_PLAYSPACE: result});
        } onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onGetQuality(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(disableAdvertising:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer disableAdvertisingOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onDisableAdvertising(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(enableAdvertising:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer enableAdvertisingOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onEnableAdvertising(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setMacros:(nonnull NSNumber *) reactTag :(NSString * _Nonnull)macrosJsonString) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        NSData *macros = [macrosJsonString dataUsingEncoding:NSUTF8StringEncoding];
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setMacros:macros onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetMacros(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setPreRollBreak:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)seconds) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setPreRollBreakWithSeconds:seconds.intValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetPreRollBreak(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setPostRollBreak:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)seconds) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setPostRollBreakWithSeconds:seconds.intValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetPostRollBreak(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(addFriendlyObstruction:(nonnull NSNumber *) reactTag :(nonnull NSNumber *) obstructionReactTag :(nonnull NSString *) purpose :(nonnull NSString *) detailedReason) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        UIView *obstructionView = [weakSelf getObstructionView:obstructionReactTag];
        CNObstructionPurpose obstructionPurpose = [ObjcHelper toObstructionPurpose:purpose];
        if (!obstructionView) {
            RCTLogError(@"Cannot find UIView with tag #%@", obstructionReactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer addFriendlyObstructionWithView:obstructionView purpose:obstructionPurpose detailedReason:detailedReason onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onAddFriendlyObstruction(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(removeFriendlyObstruction:(nonnull NSNumber *) reactTag :(nonnull NSNumber *) obstructionReactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        UIView *obstructionView = [weakSelf getObstructionView:obstructionReactTag];
        if (!obstructionView) {
            RCTLogError(@"Cannot find UIView with tag #%@", obstructionReactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer removeFriendlyObstructionWithView:obstructionView onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onRemoveFriendlyObstruction(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

// Playspace API

RCT_EXPORT_METHOD(getStoryPosition:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer getStoryPositionOnResult:^(NSInteger position) {
            weakView.onGetStoryPosition(@{RESULT_KEY_PLAYSPACE: [NSNumber numberWithLong:position]});
        } onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onGetStoryPosition(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setStoryPosition:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)index) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setStoryPosition:index.intValue onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onSetStoryPosition(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getStoryId:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer getStoryIdOnResult:^(NSString * _Nonnull storyId) {
            weakView.onGetStoryId(@{RESULT_KEY_PLAYSPACE: storyId});
        } onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onGetStoryId(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getStoryTimeline:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer getStoryTimelineOnResult:^(NSArray<NSNumber *> * _Nonnull storyTimeline) {
            weakView.onGetStoryTimeline(@{RESULT_KEY_PLAYSPACE: storyTimeline});
        } onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onGetStoryTimeline(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getLayout:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer getLayoutOnResult:^(NSInteger layout) {
            weakView.onGetLayout(@{RESULT_KEY_PLAYSPACE: [NSNumber numberWithLong:layout]});
        } onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onGetLayout(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setLayout:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)layoutType) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setLayoutWithLayoutType:layoutType.intValue onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onSetLayout(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setCtaLabel:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)slideIndex :(NSString * _Nonnull)label) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer setCtaLabelWithSlideIndex:slideIndex.intValue label:label onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onSetCtaLabel(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getStoryMetadata:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTPlayspacePlayer *) weakView = view;
        [weakView.playspacePlayer getStoryMetadataOnResult:^(NSArray<CNStoryMetadata *> * _Nonnull storyMetadata) {
            NSMutableArray *array = [[NSMutableArray alloc] init];
            for (int index = 0; index < storyMetadata.count; index++) {
                NSMutableDictionary * dictionary = [[NSMutableDictionary alloc] init];
                dictionary[@"title"] = storyMetadata[index].title;
                dictionary[@"duration"] = storyMetadata[index].duration;
                dictionary[@"clickUrl"] = storyMetadata[index].clickUrl;
                [array addObject:dictionary];
            }
            weakView.onGetStoryMetadata(@{RESULT_KEY_PLAYSPACE: array});
        } onError:^(CNPlayspaceAPIError * _Nonnull error) {
            weakView.onGetStoryMetadata(@{ERROR_KEY_PLAYSPACE: error.message});
        }];
    }];
}

// Events API

RCT_EXPORT_METHOD(listenFor:(nonnull NSNumber *) reactTag :(NSString *)event :(BOOL)once) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        @try {
            CNEventType eventType = [CNConnatixPlayer mapStringToEventType:event];
            [view.playspacePlayer listenFor:eventType :once];
        }
        @catch (NSException *exception) {
            view.onListenFor(@{ERROR_KEY_PLAYSPACE: exception.reason});
        }
    }];
}

RCT_EXPORT_METHOD(listenForMore:(nonnull NSNumber *) reactTag :(NSArray *)events) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        NSMutableArray* eventsToListenTo = [[NSMutableArray alloc] init];
        for (int index = 0; index < events.count; index++) {
            @try {
                CNEventType eventType = [CNConnatixPlayer mapStringToEventType:events[index]];
                [eventsToListenTo addObject:events[index]];
            }
            @catch (NSException *exception) {
                view.onListenForMore(@{ERROR_KEY_PLAYSPACE: exception.reason});
                return;
            }
        }
        [view.playspacePlayer listenForMore:eventsToListenTo];
    }];
}

RCT_EXPORT_METHOD(listenForAllEvents:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        [view.playspacePlayer listenForAllEvents];
    }];
}

RCT_EXPORT_METHOD(remove:(nonnull NSNumber *) reactTag :(NSString *)event) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        @try {
            CNEventType eventType = [CNConnatixPlayer mapStringToEventType:event];
            [view.playspacePlayer remove:eventType];
        }
        @catch (NSException *exception) {
            view.onRemove(@{ERROR_KEY_PLAYSPACE: exception.reason});
        }
    }];
}

RCT_EXPORT_METHOD(removeAllEvents:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTPlayspacePlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTPlayspacePlayer class]]) {
            RCTLogError(@"Cannot find RCTPlayspacePlayer with tag #%@", reactTag);
            return;
        }
        [view.playspacePlayer removeAllEvents];
    }];
}

@end
