#import "Knotapi.h"
#import <React/RCTConvert.h>
#import "KnotAPI/KnotAPI-Swift.h"
#import <WebKit/WebKit.h>

@interface Knotapi () <KnotEventDelegate>
@property (nonatomic, strong) NSString* productName;
@end

@implementation Knotapi

RCT_EXPORT_MODULE()

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getSdkVersion) {
    NSString *version = [Knot SDKVersion];
    return version ?: [NSNull null];
}

RCT_EXPORT_METHOD(close){
    dispatch_async(dispatch_get_main_queue(), ^{
        [Knot close];
    });
}

RCT_EXPORT_METHOD(open:(NSDictionary *)params){
  dispatch_async(dispatch_get_main_queue(), ^{
      self.productName = nil;
      NSString *sessionId = [params objectForKey:@"sessionId"];
      NSString *clientId = [params objectForKey:@"clientId"];
      NSArray<NSNumber*> *merchantIds = [params objectForKey:@"merchantIds"];
      NSString *environmentString = [params objectForKey:@"environment"];
      NSString *entryPoint = [params objectForKey:@"entryPoint"];

      BOOL useCategories = [[params objectForKey:@"useCategories"] boolValue];
      BOOL useSearch = true; // default value is true
      id useSearchValue = [params objectForKey:@"useSearch"];
      if (useSearchValue != nil) {
          useSearch = [useSearchValue boolValue];
      }

    NSDictionary *dict = [params objectForKey:@"customerConfig"];
    NSString *cardName = [dict[@"cardName"] isKindOfClass:[NSString class]] ? dict[@"cardName"] : nil;
    NSString *customerName = [dict[@"customerName"] isKindOfClass:[NSString class]] ? dict[@"customerName"] : nil;
    NSString *logoId = [dict[@"logoId"] isKindOfClass:[NSString class]] ? dict[@"logoId"] : nil;
    CustomerConfiguration *customerConfig = nil;
    if (cardName || customerName || logoId) {
      customerConfig = [[CustomerConfiguration alloc] initWithCardName:cardName
                                                          customerName:customerName
                                                                logoId:logoId];
    }

    Environment environment = EnvironmentProduction;
    if ([environmentString isEqualToString:@"sandbox"]) {
      environment = EnvironmentSandbox;
    }
    if ([environmentString isEqualToString:@"development"]) {
      environment = EnvironmentDevelopment;
    }

    KnotConfiguration *knotConfiguration = [[KnotConfiguration alloc]
        initWithSessionId:sessionId clientId:clientId environment:environment entryPoint:entryPoint useCategories:useCategories useSearch:useSearch merchantIds:merchantIds customerConfiguration: customerConfig
    ];

    [Knot openWithConfiguration:knotConfiguration delegate:self];

  });
}

- (NSArray<NSString *> *)supportedEvents
{
    // Original events
    NSArray *originalEvents = @[@"onSuccess", @"onError", @"onEvent", @"onExit"];

    // Mapping of original events to new names
    NSDictionary *eventMapping = @{
        @"onSuccess" : @"knot:onSuccess",
        @"onError" : @"knot:onError",
        @"onEvent" : @"knot:onEvent",
        @"onExit" : @"knot:onExit"
    };

    // Map each original event to its new name
    NSMutableArray *mappedEvents = [NSMutableArray array];
    for (NSString *event in originalEvents) {
        NSString *newEvent = eventMapping[event] ?: event; // In case the event is not found, keep the original
        [mappedEvents addObject:newEvent];
    }

    return [mappedEvents copy];
}

- (void)onErrorWithError:(enum KnotError)error {
  NSString *errorDescription;
  NSString *errorCode;
  switch (error) {
      case KnotErrorInvalidSession:
        errorCode = @"Invalid_Session";
        errorDescription = @"The session is invalid.";
        break;
      case KnotErrorInvalidCardName:
        errorCode = @"INVALID_CARD_NAME";
        errorDescription = @"The card name is invalid.";
        break;
      case KnotErrorInvalidCustomerName:
        errorCode = @"INVALID_CUSTOMER_NAME";
        errorDescription = @"The customer name is invalid.";
        break;
      case KnotErrorInvalidLogoId:
        errorCode = @"INVALID_LOGO_ID";
        errorDescription = @"The logo ID is invalid.";
        break;
      case KnotErrorExpiredSession:
        errorCode = @"Expired_Session";
        errorDescription = @"The session has expired.";
        break;
      case KnotErrorInvalidClientId:
        errorCode = @"Invalid_Client_Id";
        errorDescription = @"The client ID is invalid.";
        break;
      case KnotErrorInternalError:
        errorCode = @"Internal_Error";
        errorDescription = @"An internal error occurred.";
        break;
    case KnotErrorMerchantIdNotFound:
      errorCode = @"Merchant_Id_Not_Found";
      errorDescription = @"The merchant ID is required.";
      break;
  }
  NSMutableDictionary *body = [@{@"errorCode": errorCode, @"errorDescription": errorDescription} mutableCopy];
  if (self.productName != nil) {
    body[@"product"] = self.productName;
  }
  [self sendEventWithName:@"knot:onError" body:body];
}

- (void)onEventWithEvent:(KnotEvent * _Nonnull)event {
  Environment environment = event.environment;
  NSString *environmentString = @"";
  if (environment == EnvironmentSandbox) {
    environmentString = @"sandbox";
  } else if (environment == EnvironmentDevelopment) {
    environmentString = @"development";
  } else {
    environmentString = @"production";
  }

  NSMutableDictionary *body = [@{@"event": event.event, @"environment": environmentString, @"merchantName": event.merchant, @"merchantId": event.merchantId} mutableCopy];
  // TODO: read product from ios native side, when we have access to it
  switch (event.product) {
    case 1:
      self.productName = @"transaction_link";
      break;
    case 2:
      self.productName = @"link";
      break;
    default:
      self.productName = @"card_switcher";
      break;
  }
  if (self.productName != nil) {
    body[@"product"] = self.productName;
  }
  if (event.taskId != nil) {
    body[@"taskId"] = event.taskId;
  }
  if (event.metaData != nil) {
    body[@"metaData"] = event.metaData;
  }
  [self sendEventWithName:@"knot:onEvent" body:body];
}

- (void)onExit {
  NSMutableDictionary *body = [@{} mutableCopy];
  if (self.productName != nil) {
    body[@"product"] = self.productName;
  }
  [self sendEventWithName:@"knot:onExit" body:body];
}

- (void)onSuccessWithMerchant:(NSString * _Nonnull)merchant {
  NSMutableDictionary *body = [@{@"merchant": merchant} mutableCopy];
  if (self.productName != nil) {
    body[@"product"] = self.productName;
  }
  [self sendEventWithName:@"knot:onSuccess" body:body];
}

@end
