#import "PayUBizSdk.h"
#import <React/RCTUtils.h>
#import <PayUCheckoutProKit/PayUCheckoutProKit.h>
#import <PayUCheckoutProBaseKit/PayUCheckoutProBaseKit.h>

@interface PayUBizSdk() <PayUCheckoutProDelegate>

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

@property (nonatomic, strong) NSMutableDictionary *hashCallbacks;

@end

@implementation PayUBizSdk
{
    bool hasListeners;
}
NSString *const OnPaymentSuccess = @"onPaymentSuccess";
NSString *const OnPaymentFailure = @"onPaymentFailure";
NSString *const OnPaymentCancel = @"onPaymentCancel";
NSString *const OnError = @"onError";
NSString *const GenerateHash = @"generateHash";

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

RCT_EXPORT_METHOD(openCheckoutScreen:(NSDictionary *)params) {
    @try {
        self.hashCallbacks = [[NSMutableDictionary alloc] init];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            UIViewController *rootVc = RCTPresentedViewController();
            [PayUCheckoutPro openOn:rootVc params:params delegate:self];
        });
    }
    @catch (NSException * exception) {
        [self onError:[PayUBizSdk convertExceptionToError:exception]];
    }
}

RCT_EXPORT_METHOD(makeHttpRequest:(NSString *)url
                  method:(NSString *)method
                  body:(NSString *)body
                  headers:(NSDictionary *)headers
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @try {
            NSURL *requestURL = [NSURL URLWithString:url];
            if (!requestURL) {
                reject(@"INVALID_URL", @"Invalid URL provided", nil);
                return;
            }
            
            NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
            [request setHTTPMethod:method.uppercaseString];
            [request setTimeoutInterval:15.0];
            
            // Set default headers
            [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
            [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
            
            // Set custom headers
            if (headers) {
                for (NSString *key in headers.allKeys) {
                    NSString *value = headers[key];
                    if (value && [value isKindOfClass:[NSString class]]) {
                        [request setValue:value forHTTPHeaderField:key];
                    }
                }
            }
            
            // Set request body
            if (body && body.length > 0) {
                [request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
            }
            
            NSLog(@"PayUBizSdk: HTTP Request: %@ %@", method, url);
            NSLog(@"PayUBizSdk: Request Body: %@", body);
            
            NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
            config.timeoutIntervalForRequest = 15.0;
            config.timeoutIntervalForResource = 30.0;
            
            // Create session with custom configuration
            NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
            
            NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                if (error) {
                    NSLog(@"PayUBizSdk: HTTP Request Error: %@", error.localizedDescription);
                    reject(@"HTTP_ERROR", error.localizedDescription, error);
                    return;
                }
                
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                NSLog(@"PayUBizSdk: HTTP Response Code: %ld", (long)httpResponse.statusCode);
                
                NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                NSLog(@"PayUBizSdk: HTTP Response: %@", responseString);
                
                if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
                    resolve(responseString);
                } else {
                    NSString *errorMessage = [NSString stringWithFormat:@"HTTP %ld: %@", (long)httpResponse.statusCode, responseString];
                    reject(@"HTTP_ERROR", errorMessage, nil);
                }
            }];
            
            [task resume];
            
        } @catch (NSException *exception) {
            NSLog(@"PayUBizSdk: Exception in makeHttpRequest: %@", exception.reason);
            reject(@"HTTP_ERROR", exception.reason, nil);
        }
    });
}

#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[HashConstant.hashName]];
    [self sendEventWithName:GenerateHash body:param];
}

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

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

- (void)onPaymentFailureWithResponse:(id _Nullable)response {
    [self sendEventWithName:OnPaymentFailure body:response];
}

- (void)onPaymentSuccessWithResponse:(id _Nullable)response {
    [self sendEventWithName:OnPaymentSuccess body:response];
}

#pragma mark - RCTEventEmitter methods

-(NSArray<NSString *> *)supportedEvents {
    return @[OnPaymentSuccess, OnPaymentFailure, OnPaymentCancel, OnError, GenerateHash];
}

// 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
