/*
Copyright 2020 Appboxo pte. ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#import "Rnappboxosdk.h"
#import <UIKit/UIKit.h>
#import "BoxoSDK/BoxoSDK-Swift.h"

@interface Rnappboxosdk () <MiniappDelegate>

@end

#define APPBOXO_CUSTOM_EVENT @"appboxo_custom_events"
#define APPBOXO_PAYMENT_EVENT @"appboxo_payment_events"
#define APPBOXO_MINIAPP_LIFECYCLE_EVENT @"appboxo_miniapp_lifecycle"
#define APPBOXO_MINIAPP_LIST_EVENT @"appboxo_miniapp_list"

@implementation Rnappboxosdk

RCT_EXPORT_MODULE()


RCT_EXPORT_METHOD(setConfig:(NSString *)clientId userId:(NSString *)userId language:(NSString *)language sandboxMode:(BOOL)sandboxMode theme:(NSString *)theme isDebug:(BOOL)isDebug showPermissionsPage:(BOOL)showPermissionsPage showClearCache:(BOOL)showClearCache showAboutPage:(BOOL)showAboutPage splashScreenOptions:(nullable NSDictionary<NSString *, id> *)splashScreenOptions)
{
    NSArray *themes = @[@"dark", @"light", @"system"];

    Theme globalTheme = ThemeSystem;
    
    if ([themes containsObject:theme]) {
        globalTheme = (Theme) [themes indexOfObject:theme];
    };

    Config *config = [[Config alloc] initWithClientId: clientId theme: globalTheme];
    [config setUserId: userId];
    config.language = language;
    config.sandboxMode = sandboxMode;
    config.permissionsPage = showPermissionsPage;
    config.showClearCache = showClearCache;
    config.showAboutPage = showAboutPage;
    
    if (splashScreenOptions != NULL) {
        UIColor *lightBackground = [self colorFromHexString: splashScreenOptions[@"lightBackground"]];
        UIColor *darkBackground = [self colorFromHexString: splashScreenOptions[@"darkBackground"]];
        
        if (lightBackground != NULL && darkBackground != NULL) {
            config.splashBackgroundColors = [[SplashBackgroundColors alloc] light: lightBackground dark: darkBackground];
        }

        UIColor *lightProgressIndicator = [self colorFromHexString: splashScreenOptions[@"lightProgressIndicator"]];
        UIColor *lightProgressTrack = [self colorFromHexString: splashScreenOptions[@"lightProgressTrack"]];
        UIColor *darkProgressIndicator = [self colorFromHexString: splashScreenOptions[@"darkProgressIndicator"]];
        UIColor *darkProgressTrack = [self colorFromHexString: splashScreenOptions[@"darkProgressTrack"]];
        
        if (lightProgressIndicator != NULL && lightProgressTrack != NULL && darkProgressIndicator != NULL && darkProgressTrack != NULL) {
            config.progressBarColors = [[ProgressBarColors alloc] initWithLightIndicator: lightProgressIndicator lightTrack: lightProgressTrack darkIndicator: darkProgressIndicator darkTrack: darkProgressTrack];
        }
    }
    
    [[Boxo shared] setConfig: config];
}

RCT_EXPORT_METHOD(openMiniapp:(NSString *)appId data:(nullable NSDictionary<NSString *,id> *)data theme:(nullable NSString *)theme extraUrlParams:(nullable NSDictionary<NSString *,id> *)extraUrlParams urlSuffix:(nullable NSString *)urlSuffix colors:(nullable NSDictionary<NSString *,id> *)colors enableSplash:(BOOL)enableSplash saveState:(BOOL)saveState pageAnimation:(nullable NSString *)pageAnimation)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        Miniapp *miniapp = [[Boxo shared] getMiniappWithAppId: appId];
        [miniapp setData: data];
        [miniapp setDelegate:self];

        MiniappConfig *miniappConfig = [[MiniappConfig alloc] init];
        [miniappConfig enableSplash: enableSplash];
        [miniappConfig setExtraParams:extraUrlParams];
        miniappConfig.saveState = saveState;
        
        if (pageAnimation != NULL) {
            if ([pageAnimation isEqualToString:@"TOP_TO_BOTTOM"]) {
                miniappConfig.pageAnimation = PageAnimationTOP_TO_BOTTOM;
            } else if ([pageAnimation isEqualToString:@"LEFT_TO_RIGHT"]) {
                miniappConfig.pageAnimation = PageAnimationLEFT_TO_RIGHT;
            } else if ([pageAnimation isEqualToString:@"RIGHT_TO_LEFT"]) {
                miniappConfig.pageAnimation = PageAnimationRIGHT_TO_LEFT;
            } else if ([pageAnimation isEqualToString:@"FADE_IN"]) {
                miniappConfig.pageAnimation = PageAnimationFADE_IN;
            } else {
                miniappConfig.pageAnimation = PageAnimationBOTTOM_TO_TOP;
            }
        }

        if (urlSuffix != NULL) {
            miniappConfig.urlSuffix = urlSuffix;
        }

        if (theme != NULL) {
            NSArray *themes = @[@"dark", @"light", @"system"];
            if ([themes containsObject:theme]) {
                [miniappConfig setTheme:(Theme) [themes indexOfObject:theme]];
            }
        }
        
        if (colors != NULL) {
            NSString *primaryColor = colors[@"primaryColor"] != NULL ? (NSString *) colors[@"primaryColor"] : @"";
            NSString *secondaryColor = colors[@"secondaryColor"] != NULL ? (NSString *) colors[@"secondaryColor"] : @"";
            NSString *tertiaryColor = colors[@"tertiaryColor"] != NULL ? (NSString *) colors[@"tertiaryColor"] : @"";
            
            MiniappColor *color = [[MiniappColor alloc] initWithPrimary: primaryColor secondary: secondaryColor tertiary: tertiaryColor];
            [miniappConfig setColor: color];
        }

        [miniapp setConfig: miniappConfig];
        [miniapp openWithViewController: [UIApplication sharedApplication].delegate.window.rootViewController];
    });
}

RCT_EXPORT_METHOD(closeMiniapp:(NSString *)appId)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        Miniapp *miniapp = [[Boxo shared] getMiniappWithAppId: appId];
        [miniapp close];
    });
}

RCT_EXPORT_METHOD(setAuthCode:(NSString *)appId code:(NSString *)code)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        Miniapp *miniapp = [[Boxo shared] getMiniappWithAppId: appId];
        [miniapp setAuthCode: code];
    });
}

RCT_EXPORT_METHOD(setAuthTokens:(NSString *)appId tokens:(NSDictionary<NSString *,id> * _Nonnull)tokens)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        Miniapp *miniapp = [[Boxo shared] getMiniappWithAppId: appId];
        [miniapp setAuthTokens: tokens];
    });
}

RCT_EXPORT_METHOD(hideMiniapps)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [[Boxo shared] hideMiniapps];
    });
}

RCT_EXPORT_METHOD(logout)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [[Boxo shared] logout];
    });
}

RCT_EXPORT_METHOD(sendCustomEvent:(NSDictionary<NSString *,id> * _Nonnull)params)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSString *appId = params[@"app_id"] != NULL ? params[@"app_id"] : @"";
        NSDictionary<NSString *, id> *customEventParams = params[@"custom_event"];
        
        CustomEvent *customEvent = [CustomEvent new];
        customEvent.requestId = customEventParams[@"request_id"] != NULL ? (NSInteger) customEventParams[@"request_id"] : 0;
        customEvent.type = customEventParams[@"type"] != NULL ? (NSString *) customEventParams[@"type"] : @"";
        customEvent.errorType = customEventParams[@"error_type"] != NULL ? (NSString *) customEventParams[@"error_type"] : @"";
        customEvent.payload = customEventParams[@"payload"] != NULL ? (NSDictionary<NSString *, id> *) customEventParams[@"payload"] : NULL;
        
        Miniapp *miniapp = [[Boxo shared] getMiniappWithAppId:appId];
        if (miniapp != NULL) {
            [miniapp sendCustomEvent:customEvent];
        };
    });
}

RCT_EXPORT_METHOD(sendPaymentEvent:(NSDictionary<NSString *,id> * _Nonnull)params)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSString *appId = params[@"app_id"] != NULL ? params[@"app_id"] : @"";
        NSDictionary<NSString *, id> *paymentEventParams = params[@"payment_event"];
        
        PaymentData *paymentData = [PaymentData new];
        paymentData.transactionToken = paymentEventParams[@"transaction_token"] != NULL ? (NSString *) paymentEventParams[@"transaction_token"] : @"";
        paymentData.orderPaymentId = paymentEventParams[@"order_payment_id"] != NULL ? (NSString *) paymentEventParams[@"order_payment_id"] : @"";
        paymentData.miniappOrderId = paymentEventParams[@"miniapp_order_id"] != NULL ? (NSString *) paymentEventParams[@"miniapp_order_id"] : @"";
        paymentData.amount = paymentEventParams[@"amount"] != NULL ? [paymentEventParams[@"amount"] doubleValue] : 0;
        paymentData.currency = paymentEventParams[@"currency"] != NULL ? (NSString *) paymentEventParams[@"currency"] : @"";
        paymentData.status = paymentEventParams[@"status"] != NULL ? (NSString *) paymentEventParams[@"status"] : @"";
        paymentData.hostappOrderId = paymentEventParams[@"hostapp_order_id"] != NULL ? (NSString *) paymentEventParams[@"hostapp_order_id"] : @"";
        paymentData.extraParams = paymentEventParams[@"extra_params"] != NULL ? (NSDictionary<NSString *, id> *) paymentEventParams[@"extra_params"] : NULL;
        
        Miniapp *miniapp = [[Boxo shared] getMiniappWithAppId:appId];
        if (miniapp != NULL) {
            [miniapp sendPaymentEvent: paymentData];
        };
    });
}

RCT_EXPORT_METHOD(getMiniapps)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [[Boxo shared] getMiniapps:^(NSArray<MiniappData *> * _Nonnull list, NSString * _Nullable error) {
            if (error != NULL) {
                [self sendEventWithName:APPBOXO_MINIAPP_LIST_EVENT body:@{@"error": error}];
            } else {
                NSMutableArray *array = [NSMutableArray new];
                for (int i = 0; i < list.count; i++) {
                    MiniappData *miniappData = list[i];
                    NSMutableDictionary *data = [NSMutableDictionary new];
                    data[@"app_id"] = miniappData.appId;
                    data[@"name"] = miniappData.name;
                    data[@"category"] = miniappData.category;
                    data[@"logo"] = miniappData.logo;
                    data[@"description"] = miniappData.longDescription;
                    array[i] = data;
                }

                [self sendEventWithName:APPBOXO_MINIAPP_LIST_EVENT body:@{@"miniapps": array}];
            }
        }];
    });
}

- (NSArray<NSString *> *)supportedEvents
{
    return @[APPBOXO_CUSTOM_EVENT, APPBOXO_PAYMENT_EVENT, APPBOXO_MINIAPP_LIFECYCLE_EVENT, APPBOXO_MINIAPP_LIST_EVENT];
}

- (void)didReceiveCustomEvent:(Miniapp *)miniapp customEvent:(CustomEvent *)customEvent {
    NSMutableDictionary *customEventParams = [NSMutableDictionary new];
    customEventParams[@"request_id"] = [NSNumber numberWithLong: customEvent.requestId];
    customEventParams[@"type"] = customEvent.type;
    customEventParams[@"error_type"] = customEvent.errorType;

    if (customEvent.payload) {
        customEventParams[@"payload"] = customEvent.payload;
    }

    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"custom_event": customEventParams
    };

    [self sendEventWithName:APPBOXO_CUSTOM_EVENT body:body];
}

- (void)didReceivePaymentEvent:(Miniapp *)miniapp paymentData:(PaymentData *)paymentData {
    NSMutableDictionary *paymentEventParams = [NSMutableDictionary new];
    paymentEventParams[@"transaction_token"] = paymentData.transactionToken;
    paymentEventParams[@"order_payment_id"] = paymentData.orderPaymentId;
    paymentEventParams[@"miniapp_order_id"] = paymentData.miniappOrderId;
    paymentEventParams[@"amount"] = [NSNumber numberWithDouble:paymentData.amount];
    paymentEventParams[@"currency"] = paymentData.currency;
    paymentEventParams[@"status"] = paymentData.status;
    paymentEventParams[@"hostapp_order_id"] = paymentData.hostappOrderId;

    if (paymentData.extraParams) {
        paymentEventParams[@"extra_params"] = paymentData.extraParams;
    }

    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"payment_event": paymentEventParams
    };

    [self sendEventWithName:APPBOXO_PAYMENT_EVENT body:body];
}

- (void)onLaunchMiniapp:(Miniapp *)miniapp {
    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"lifecycle": @"onLaunch"
    };

    [self sendEventWithName:APPBOXO_MINIAPP_LIFECYCLE_EVENT body:body];
}

- (void)onResumeMiniapp:(Miniapp *)miniapp {
    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"lifecycle": @"onResume"
    };

    [self sendEventWithName:APPBOXO_MINIAPP_LIFECYCLE_EVENT body:body];
}

- (void)onPauseMiniapp:(Miniapp *)miniapp {
    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"lifecycle": @"onPause"
    };

    [self sendEventWithName:APPBOXO_MINIAPP_LIFECYCLE_EVENT body:body];
}

- (void)onCloseMiniapp:(Miniapp *)miniapp {
    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"lifecycle": @"onClose"
    };

    [self sendEventWithName:APPBOXO_MINIAPP_LIFECYCLE_EVENT body:body];
}

- (void)onErrorMiniapp:(Miniapp *)miniapp message:(NSString *)message {
    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"lifecycle": @"onError",
        @"error": message
    };

    [self sendEventWithName:APPBOXO_MINIAPP_LIFECYCLE_EVENT body:body];
}

- (void)onAuthMiniapp:(Miniapp *)miniapp {
    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"lifecycle": @"onAuth"
    };
    [self sendEventWithName:APPBOXO_MINIAPP_LIFECYCLE_EVENT body:body];
}

- (void)onUserInteraction:(Miniapp *)miniapp {
    NSDictionary *body = @{
        @"app_id": miniapp.appId,
        @"lifecycle": @"onUserInteraction"
    };
    [self sendEventWithName:APPBOXO_MINIAPP_LIFECYCLE_EVENT body:body];
}

- (nullable UIColor *)colorFromHexString: (nullable id) hexString {
    if (hexString != NULL && [hexString isKindOfClass:[NSString class]]) {
        NSString *hex = [[hexString stringByTrimmingCharactersInSet:
                          [NSCharacterSet whitespaceAndNewlineCharacterSet]]
                         stringByReplacingOccurrencesOfString:@"#" withString:@""];
        
        unsigned long long intValue = 0;
        NSScanner *scanner = [NSScanner scannerWithString:hex];
        if (![scanner scanHexLongLong:&intValue]) {
            return nil;
        }
        
        CGFloat red, green, blue, alpha;
        
        switch (hex.length) {
            case 6: { // RRGGBB
                red   = ((intValue >> 16) & 0xFF) / 255.0;
                green = ((intValue >> 8)  & 0xFF) / 255.0;
                blue  = ( intValue        & 0xFF) / 255.0;
                alpha = 1.0;
                break;
            }
                
            case 8: { // RRGGBBAA
                red   = ((intValue >> 24) & 0xFF) / 255.0;
                green = ((intValue >> 16) & 0xFF) / 255.0;
                blue  = ((intValue >> 8)  & 0xFF) / 255.0;
                alpha = ( intValue        & 0xFF) / 255.0;
                break;
            }
                
            case 3: { // RGB short form
                unsigned long long r = ((intValue >> 8) & 0xF) * 17;
                unsigned long long g = ((intValue >> 4) & 0xF) * 17;
                unsigned long long b = ( intValue       & 0xF) * 17;
                
                red   = r / 255.0;
                green = g / 255.0;
                blue  = b / 255.0;
                alpha = 1.0;
                break;
            }
                
            default:
                return NULL;
        }
        
        return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
    }
    
    return NULL;
}

@end
