
#import "RNGeniusScan.h"

#import <React/RCTConvert.h>
#import <React/RCTUtils.h>
#import <GSSDK/GSSDK.h>
#import <GSSDK/GSSDK-Swift.h>

@interface RNGeniusScan()
@property (nonatomic, strong) GSKScanFlow *scanner;
@property (nonatomic, strong) GSKBarcodeScanFlow *barcodeScanner;
- (void)presentViewController:(UIViewController *)viewController rejecter:(RCTPromiseRejectBlock)reject;
- (void)rejectWithError:(NSError *)error rejecter:(RCTPromiseRejectBlock)reject;
@end

@implementation RNGeniusScan

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

- (void)presentViewController:(UIViewController *)viewController rejecter:(RCTPromiseRejectBlock)reject {
    dispatch_async(dispatch_get_main_queue(), ^{
        UIViewController *root = RCTPresentedViewController();
        @try {
            [root presentViewController:viewController animated:YES completion:nil];
        }
        @catch (NSException *exception) {
            reject(exception.name, exception.reason, nil);
        }
    });
}

- (NSError *)fallbackInternalErrorWithMessage:(NSString *)message {
    return [NSError errorWithDomain:GSKScanFlowErrorDomain
                               code:GSKScanFlowInternalErrorCode
                           userInfo:@{ NSLocalizedDescriptionKey: message ?: @"" }];
}

- (void)rejectWithError:(NSError *)error rejecter:(RCTPromiseRejectBlock)reject {
    NSDictionary<NSString *, NSString *> *payload = [GSKScanFlowErrorPayload payloadFromNSError:error];
    NSMutableDictionary<NSString *, id> *userInfo = [payload mutableCopy];
    NSError *wrappedError = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
    reject(payload[@"code"], payload[@"message"], wrappedError);
}

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(setLicenseKey:(NSString *)licenseKey autoRefresh:(BOOL)autoRefresh resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    [GSKIntegrationContext setFramework:GSKIntegrationFrameworkReactNative];
    [GSK setLicenseKey:licenseKey autoRefresh:autoRefresh];
    resolve(nil);
}

RCT_EXPORT_METHOD(scanWithConfiguration:(NSDictionary *)scanOptions
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject
                  ) {
    NSError *error = nil;
    GSKScanFlowConfiguration *configuration = [GSKScanFlowConfiguration configurationWithDictionary:scanOptions error:&error];
    if (!configuration) {
        [self rejectWithError:error rejecter:reject];
        return;
    }

    self.scanner = [GSKScanFlow scanFlowWithConfiguration:configuration];

    dispatch_async(dispatch_get_main_queue(), ^{
        UIViewController *root = RCTPresentedViewController();
        @try {
            [self.scanner startFromViewController:root onSuccess:^(GSKScanFlowResult * _Nonnull result) {
                resolve([result dictionary]);
            } failure:^(NSError *error) {
                [self rejectWithError:error rejecter:reject];
            }];
        }
        @catch (NSException *exception) {
            reject(exception.name, exception.reason, nil);
        }
    });
}

RCT_EXPORT_METHOD(generateDocument:(NSDictionary *)documentDictionary
                  withConfiguration:(NSDictionary *)configurationDictionary
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject
                  ) {
    NSError *error = nil;
    GSKDocumentGeneratorConfiguration *configuration = [[GSKDocumentGeneratorConfiguration alloc] initWithDictionary:configurationDictionary error:&error];
    if (!configuration) {
        [self rejectWithError:error rejecter:reject];
        return;
    }

    GSKPDFDocument *document = [[GSKPDFDocument alloc] initWithDictionary:documentDictionary error:&error];
    if (!document) {
        [self rejectWithError:error rejecter:reject];
        return;
    }

    BOOL success = [[GSKDocumentGenerator alloc] generate:document configuration:configuration error:&error];
    if (success) {
        resolve(nil);
    } else {
        NSError *finalError = error ?: [self fallbackInternalErrorWithMessage:@"Document generation failed."];
        [self rejectWithError:finalError rejecter:reject];
    }
}

RCT_EXPORT_METHOD(scanBarcodesWithConfiguration:(NSDictionary *)configuration
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject
                  ) {
    NSError *error = nil;
    GSKBarcodeScanFlowConfiguration *barcodeConfiguration = [GSKBarcodeScanFlowConfiguration configurationWithDictionary:configuration error:&error];
    if (!barcodeConfiguration) {
        [self rejectWithError:error rejecter:reject];
        return;
    }

    self.barcodeScanner = [[GSKBarcodeScanFlow alloc] initWithConfiguration:barcodeConfiguration];

    dispatch_async(dispatch_get_main_queue(), ^{
        UIViewController *root = RCTPresentedViewController();
        @try {
            [self.barcodeScanner startFromViewController:root success:^(GSKBarcodeScanFlowResult * _Nonnull result) {
                resolve([result dictionary]);
            } failure:^(NSError * _Nonnull error) {
                [self rejectWithError:error rejecter:reject];
            }];
        }
        @catch (NSException *exception) {
            reject(exception.name, exception.reason, nil);
        }
    });
}

@end
