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

@interface PayUUPIBoltUISdk()<PayUUPIBoltUIDelegate>
@end

@implementation PayUUPIBoltUISdk
{
    bool hasListeners;
}
NSString *const onPayUSuccess = @"onPayUSuccess";
NSString *const onPayUFailure = @"onPayUFailure";
NSString *const onPayUCancel = @"onPayUCancel";
NSString *const generateHash = @"generateHash";
NSString *const permissionCallback = @"permissionCallback";

PayUUPIBoltUIInterface *boltUI;
PayUUPIBoltUIHybridResponseTransformer *responseTransformer;

RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(hashGenerated:(NSDictionary *)hashDict) {
    @try {
        [responseTransformer hashGeneratedWithArgs:hashDict errorCallback:^(NSError * _Nullable error) {
            if (error) {
                [self onPayUFailureWithMessage:[error localizedDescription]];
            }
        }];
    }
    @catch (NSException * exception) {
        [self onPayUFailureWithMessage:[PayUUPIBoltUISdk convertExceptionToErrorMessage:exception]];
    }
    
}

// DMRC is using this method
RCT_EXPORT_METHOD(isUPIBoltSDKAvailable:(RCTResponseSenderBlock)onCallback) {
    if (!boltUI) {
        [self onBoltInitUError];
        return;
    }
    @try {
        dispatch_async(dispatch_get_main_queue(), ^{
            [boltUI isUPIBoltEnabledWithCallback:^(PayUUPIBoltResponse * _Nonnull response) {
                BOOL sdkAvailable = (response.code == 0);
                NSDictionary *result = @{@"isSDKAvailable": sdkAvailable ? @"true" : @"false"};
                onCallback(@[result]);
            }];
        });
    }
    @catch (NSException * exception) {
        [self onPayUFailureWithMessage:[PayUUPIBoltUISdk convertExceptionToErrorMessage:exception]];
    }
}

RCT_EXPORT_METHOD(isUPIBoltEnabled:(RCTResponseSenderBlock)onCallback) {
    if (!boltUI) {
        [self onBoltInitUError];
        return;
    }
    @try {
        dispatch_async(dispatch_get_main_queue(), ^{
            [boltUI isUPIBoltEnabledWithCallback:^(PayUUPIBoltResponse * _Nonnull response) {
                NSDictionary *result = [PayUUPIBoltUIHybridUtils handlerResponseWithResponse:response];
                onCallback(@[result]);
            }];
        });
    }
    @catch (NSException * exception) {
        [self onPayUFailureWithMessage:[PayUUPIBoltUISdk convertExceptionToErrorMessage:exception]];
    }
}

RCT_EXPORT_METHOD(isRegistered:(NSString *)pg onCallback:(RCTResponseSenderBlock)onCallback) {
    @try {
        dispatch_async(dispatch_get_main_queue(), ^{
            BOOL isRegistered = [PayUUPIBoltUI isRegisteredWith:pg];
            NSDictionary *result = @{@"isRegistered": isRegistered ? @"true" : @"false"};
            onCallback(@[result]);
        });
    }
    @catch (NSException * exception) {
        [self onPayUFailureWithMessage:[PayUUPIBoltUISdk convertExceptionToErrorMessage:exception]];
    }
}

RCT_EXPORT_METHOD(clearCache:(NSString *)pg) {
    @try {
        dispatch_async(dispatch_get_main_queue(), ^{
            [PayUUPIBoltUI clearCacheWith:pg];
        });
    }
    @catch (NSException * exception) {
        [self onPayUFailureWithMessage:[PayUUPIBoltUISdk convertExceptionToErrorMessage:exception]];
    }
}

RCT_EXPORT_METHOD(registerAndPay:(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 onPayUFailureWithMessage:[PayUUPIBoltUISdk convertExceptionToErrorMessage:exception]];
    }
}

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

RCT_EXPORT_METHOD(initSDK:(NSDictionary *)config) {
    @try {
        if(!boltUI) {
            UIViewController *rootVc = RCTPresentedViewController();
            NSMutableDictionary* payUConfig = [config mutableCopy];
            NSString *refId = payUConfig[@"refId"];
            if (refId == nil || [refId isEqualToString:@""]) {
                payUConfig[@"refId"] = config[@"requestId"];
            }
            boltUI = [PayUUPIBoltUI initSDKWithParentVC:rootVc configJSON:payUConfig delegate: self];
        }
        responseTransformer = [PayUUPIBoltUIHybridResponseTransformer new];
    }
    @catch (NSException * exception) {
        [self onPayUFailureWithMessage:[PayUUPIBoltUISdk convertExceptionToErrorMessage:exception]];
    }
}


#pragma mark - PayUCheckoutProDelegate Methods
- (void)generateHashFor:(NSDictionary<NSString *,NSString *> * _Nonnull)param onCompletion:(void (^ _Nonnull)(NSDictionary<NSString *,NSString *> * _Nonnull))onCompletion {
    [self sendEventWithName:generateHash body:[responseTransformer generateHashFor:param onCompletion:onCompletion]];
}

- (void)onPayUCancelWithIsTxnInitiated:(BOOL)isTxnInitiated {
    [self sendEventWithName:onPayUCancel body:[responseTransformer onPayUCancelWithIsTxnInitiated:isTxnInitiated]];
}

- (void)onPayUFailureWithResponse:(PayUUPIBoltResponse *)response {
    [self sendEventWithName:onPayUFailure body:[responseTransformer onPayUFailureWithResponse:response]];
}

- (void)onPayUSuccessWithResponse:(PayUUPIBoltResponse *)response {
    [self sendEventWithName:onPayUSuccess body:[responseTransformer onPayUSuccessWithResponse:response]];
}

- (void)onBoltInitUError {
    [self onPayUFailureWithMessage:@"Please initiate the UPI bolt sdk before calling this service"];
}

- (void)onPayUFailureWithMessage:(NSString *)message {
    NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
                               message, @"message",
                               @1, @"code",
                               nil];
    [self sendEventWithName:onPayUFailure body:errorDict];
}

#pragma mark - RCTEventEmitter methods

-(NSArray<NSString *> *)supportedEvents {
    return @[onPayUSuccess, onPayUFailure, onPayUCancel, 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];
    }
}

+(NSString *)convertExceptionToErrorMessage:(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.boltReactNativeWrapper" code:100 userInfo:info];
    return [error localizedDescription];
}

@end
