#import "PayUUPIBoltUISdk.h"
#import <React/RCTUtils.h>
@import PayUUPIBoltBaseKit;
@import PayUUPIBoltKit;
#import "PayUUPIBoltUIKit/PayUUPIBoltUIKit-Swift.h"



@interface PayUUPIBoltUISdk()<PayUUPIBoltUIDelegate>

typedef void (^hashCompletionCallback)(NSDictionary<NSString *,NSString *> * _Nonnull);

@property (nonatomic, strong) NSMutableDictionary *hashCallbacks;

@end

@implementation PayUUPIBoltUISdk
{
    bool hasListeners;
}
NSString *const onPayUSuccess = @"onPayUSuccess";
NSString *const onPayUFailure = @"onPayUFailure";
NSString *const onPayUCancel = @"onPayUCancel";
NSString *const OnError = @"onError";
NSString *const GenerateHash = @"generateHash";
NSString *const permissionCallback = @"permissionCallback";
PayUUPIBoltUIInterface *boltUI;

RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(hashGenerated:(NSDictionary *)hashDict) {
    @try {
        dispatch_async(dispatch_get_main_queue(), ^{
            hashCompletionCallback callback = [self.hashCallbacks objectForKey:hashDict[@"hashName"]];
            if (callback && hashDict) {
                callback(hashDict);
                [self.hashCallbacks removeObjectForKey:hashDict[@"hashName"]  ];
            }
        });
    } @catch (NSException *exception) {
        [self onPayUError:[PayUUPIBoltUISdk convertExceptionToError:exception]];
    }
}

RCT_EXPORT_METHOD(isUPIBoltSDKAvailable:(RCTResponseSenderBlock)onCallback) {
    if (!boltUI) {
        [self onBoltInitUError];
        return;
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        [boltUI isUPIBoltEnabledWithCallback:^(PayUUPIBoltResponse * _Nonnull response) {
            BOOL sdkAvailable = YES;
            if (response.code == 0) {
                sdkAvailable = YES;
            } else {
                sdkAvailable = NO;
            }
            NSDictionary *result = @{@"isSDKAvailable": sdkAvailable ? @"true" : @"false"};
            onCallback(@[result]);
        }];
    });
}

RCT_EXPORT_METHOD(isRegistered:(NSString *)pg onCallback:(RCTResponseSenderBlock)onCallback) {
    dispatch_async(dispatch_get_main_queue(), ^{
        BOOL isRegistered = [PayUUPIBoltUI isRegisteredWith:pg];
        NSDictionary *result = @{@"isRegistered": isRegistered ? @"true" : @"false"};
        onCallback(@[result]);
    });
}

RCT_EXPORT_METHOD(clearCache:(NSString *)pg) {
    dispatch_async(dispatch_get_main_queue(), ^{
        [PayUUPIBoltUI clearCacheWith:pg];
    });
}

RCT_EXPORT_METHOD(payURegisterAndPay:(NSDictionary *)params) {
    @try {
        if (!boltUI) {
            [self onBoltInitUError];
            return;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            NSMutableDictionary* payUParam = [params mutableCopy];
            payUParam[@"transactionId"] = params[@"txnId"];
            [boltUI registerAndPayWithPaymentParamsJSON:payUParam];
        });
    }
    @catch (NSException * exception) {
        [self onPayUError:[PayUUPIBoltUISdk convertExceptionToError:exception]];
    }
}

RCT_EXPORT_METHOD(payUUPIBoltUserSettings:(NSString *) screenType) {
    @try {
        if (!boltUI) {
            [self onBoltInitUError];
            return;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [boltUI openUPIManagementWithScreenTypeString:screenType];
        });
    }
    @catch (NSException * exception) {
        [self onPayUError:[PayUUPIBoltUISdk convertExceptionToError:exception]];
    }
}

RCT_EXPORT_METHOD(initSDK:(NSDictionary *)config) {
    @try {
        if (!_hashCallbacks) {
            self.hashCallbacks = [[NSMutableDictionary alloc] init];
        }
        if(!boltUI) {
            UIViewController *rootVc = RCTPresentedViewController();
            NSMutableDictionary* payUConfig = [config mutableCopy];
            payUConfig[@"refId"] = config[@"requestId"];
            boltUI = [PayUUPIBoltUI initSDKWithParentVC:rootVc configJSON:payUConfig delegate: self];
        }
    }
    @catch (NSException * exception) {
        [self onPayUError:[PayUUPIBoltUISdk convertExceptionToError:exception]];
    }
}


#pragma mark - PayUCheckoutProDelegate Methods
- (void)generateHashFor:(NSDictionary<NSString *,NSString *> * _Nonnull)param onCompletion:(void (^ _Nonnull)(NSDictionary<NSString *,NSString *> * _Nonnull))onCompletion {
    [self.hashCallbacks setObject:onCompletion forKey:param[@"hashName"]];
    [self sendEventWithName:GenerateHash body:param];
}

- (void)onBoltInitUError {
    NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
                               @"Please initiate the UPI bolt sdk before calling this service", @"errorMsg",
                               @"-2", @"errorCode",
                               nil];
    [self sendEventWithName:onPayUFailure body:errorDict];
}

- (void)onPayUError:(NSError * _Nullable)error {
    NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
                               error.localizedDescription, @"errorMsg",
                               //                               error.code, @"errorCode",
                               nil];
    [self sendEventWithName:onPayUFailure body:errorDict];
}

- (void)onPayUCancelWithIsTxnInitiated:(BOOL)isTxnInitiated {
    NSDictionary *response = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSNumber numberWithBool:isTxnInitiated], @"isTxnInitiated",
                              nil];
    [self sendEventWithName:onPayUCancel body:response];
}

- (void)onPayUFailureWithResponse:(PayUUPIBoltResponse *)response {
  NSMutableDictionary *dict = [NSMutableDictionary new];
  [dict setValue: @(response.code) forKey:@"code"];
  [dict setValue:response.message forKey:@"message"];
  [dict setValue:response.result forKey:@"result"];
  [self sendEventWithName:onPayUFailure body:dict];
}

- (void)onPayUSuccessWithResponse:(PayUUPIBoltResponse *)response {
  NSMutableDictionary *dict = [NSMutableDictionary new];
  [dict setValue: @(response.code) forKey:@"code"];
  [dict setValue:response.message forKey:@"message"];
  [dict setValue:response.result forKey:@"result"];
  [self sendEventWithName:onPayUSuccess body:dict];
}

#pragma mark - RCTEventEmitter methods

-(NSArray<NSString *> *)supportedEvents {
    return @[onPayUSuccess, onPayUFailure, onPayUCancel, OnError, GenerateHash, permissionCallback];
}

// Will be called when this module's first listener is added.
-(void)startObserving {
    hasListeners = YES;
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
    hasListeners = NO;
}

- (void)sendEventWithName:(NSString *)name body:(id)body{
    if (hasListeners) {
        [super sendEventWithName:name body:body];
    }
}

+(NSError *)convertExceptionToError:(NSException *)exception{
    NSMutableDictionary * info = [NSMutableDictionary dictionary];
    [info setValue:exception.name forKey:@"ExceptionName"];
    [info setValue:exception.reason forKey:@"ExceptionReason"];
    [info setValue:exception.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"];
    [info setValue:exception.callStackSymbols forKey:@"ExceptionCallStackSymbols"];
    [info setValue:exception.userInfo forKey:@"ExceptionUserInfo"];
    
    NSError *error = [[NSError alloc] initWithDomain:@"com.payu.bizReactNativeWrapper" code:100 userInfo:info];
    return error;
}

@end
