#import "PluginEngagementReactNative.h"
#import <React/RCTBridgeModule.h> // RCTPromiseResolveBlock / RCTPromiseRejectBlock

// Support both static and dynamic framework contexts
// Static frameworks (Expo with use_frameworks! :linkage => :static) use modular header path
#if __has_include(<PluginEngagementReactNative/PluginEngagementReactNative-Swift.h>)
#import <PluginEngagementReactNative/PluginEngagementReactNative-Swift.h>
#else
#import "PluginEngagementReactNative-Swift.h"
#endif

//@interface RCT_EXTERN_MODULE(AmplitudeEngagementAdapter, NSObject)

@implementation PluginEngagementReactNative {
  AmplitudeEngagementAdapter *adapter;
}
//RCT_EXPORT_MODULE()


- (id) init {
  if (self = [super init]) {
    adapter = [AmplitudeEngagementAdapter new];
  }
  return self;
}

// TODO: binding for AmplitudeEngagement here

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
    (const facebook::react::ObjCTurboModule::InitParams &)params
{
    return std::make_shared<facebook::react::NativePluginEngagementReactNativeSpecJSI>(params);
}

+ (NSString *)moduleName { 
  return @"PluginEngagementReactNative";
}

- (void)boot:(double)id user_id:(NSString *)user_id device_id:(NSString *)device_id user_properties:(NSDictionary *)user_properties {
  void (^callback)(NSString *, NSDictionary *) = ^(NSString *event_type, NSDictionary *event) {
    [self emitOnTrackEvent:@{@"event_type": event_type, @"event_properties": event}];
  };

  [adapter boot:id :user_id device_id:device_id user_properties:user_properties integration:callback];
}

- (void)enable:(double)id {
  [adapter enable:id];
}

- (void)disable:(double)id {
  [adapter disable:id];
}

- (void)shutdown:(double)id {
  [adapter shutdown:id];
}

- (nonnull NSArray<NSDictionary *> *)list:(double)id {
  return [adapter list:id];
}

- (void)newInstance:(nonnull NSString *)apiKey
            options:(NSDictionary *)options
            resolve:(RCTPromiseResolveBlock)resolve
             reject:(RCTPromiseRejectBlock)reject {
  // Promise-returning TurboModule method: run off the JS thread so the blocking
  // init work (e.g. fetchMinimumSDKVersion) does not stall app launch. Calls
  // made before this resolves are queued on the JS side (AmplitudeEngagement.id
  // is a Promise) and replay once the id is available.
  __weak __typeof(self) weakSelf = self;
  dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
    __typeof(self) strongSelf = weakSelf;
    if (strongSelf == nil) {
      reject(@"newInstance_error", @"PluginEngagementReactNative deallocated", nil);
      return;
    }
    NSInteger identifier = [strongSelf->adapter newInstance:apiKey :options];
    resolve(@(identifier));
  });
}

- (NSNumber*)handleURL:(double)id url:(NSString *)url {
  BOOL success = [adapter handleUrl:id :url];
  return @(success);
}

- (void)addCallback:(double)id key:(nonnull NSString *)key {
  void (^callback)(void) = ^{
    [self emitOnInvokeCallback:@{@"id": @(id), @"key": key}];
  };

  [adapter addCallback:id :key :callback];
}

- (void)setRouter:(double)id {
  void (^callback)(NSString *) = ^(NSString *url) {
    [self emitOnInvokeRouter:@{@"id": @(id), @"url": url ?: @""}];
  };

  [adapter setRouter:id :callback];
}

- (void)unsetRouter:(double)id {
  [adapter unsetRouter:id];
}


- (void)closeAll:(double)id { 
  [adapter closeAll:id];
}


- (void)forwardEvent:(double)id event:(nonnull NSDictionary *)event { 
  [adapter forwardEvent:id :event];
}


- (void)reset:(double)id key:(nonnull NSString *)key stepIndex:(double)stepIndex { 
  [adapter reset:id key:key stepIndex:stepIndex];
}


- (void)screen:(double)id screenName:(nonnull NSString *)screenName { 
  [adapter screen:id :screenName];
}


- (void)setThemeMode:(double)id themeMode:(nonnull NSString *)themeMode { 
  [adapter setThemeMode:id themeMode:themeMode];
}


- (void)show:(double)id key:(nonnull NSString *)key stepIndex:(double)stepIndex { 
  [adapter show:id key:key stepIndex:stepIndex];
}

@end
