#import "MobilesdkModule.h"
#import <IdensicMobileSDK/IdensicMobileSDK.h>

@interface MobilesdkModule ()
@property (nonatomic, copy) void(^tokenExpirationOnComplete)(NSString * _Nullable newAccessToken);
@property (nonatomic, weak) SNSMobileSDK *sdk;
@end

@implementation MobilesdkModule

RCT_EXPORT_MODULE(SNSMobileSDKModule)

- (NSArray<NSString *> *)supportedEvents
{
    return @[@"onTokenExpired", @"onLog", @"onStatusChanged", @"onEvent"];
}

RCT_EXPORT_METHOD(launch:(NSString *)apiUrl
                  accessToken:(NSString *)accessToken 
                  locale:(NSString *)locale
                  debug:(BOOL)debug
                  hasHandlers:(NSDictionary *)hasHandlers
                  applicantConf:(NSDictionary *)applicantConf
                  preferredDocumentDefinitions:(NSDictionary *)preferredDocumentDefinitions
                  theme:(NSDictionary *)theme
                  settings:(NSDictionary *)settings
                  strings:(NSDictionary *)strings
                  isAnalyticsEnabled:(BOOL)isAnalyticsEnabled
                  autoCloseOnApprove:(nonnull NSNumber *)autoCloseOnApprove
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)
{
    SNSEnvironment environment = (apiUrl && apiUrl.length > 0) ? apiUrl : SNSEnvironmentProduction;

    SNSMobileSDK *sdk = [SNSMobileSDK setupWithAccessToken:accessToken
                                               environment:environment];
    
    if (locale) {
        sdk.locale = locale;
    }

    if (![hasHandlers isKindOfClass:NSDictionary.class]) {
        hasHandlers = @{};
    }
    if (![applicantConf isKindOfClass:NSDictionary.class]) {
        applicantConf = @{};
    }

    if (!sdk.isReady) {
        return resolve([self makeResult:sdk]);
    }
    
    __weak typeof(self) weakSelf = self;
    
    self.sdk = sdk;

    if (debug) {
        sdk.logLevel = SNSLogLevel_Debug;
    }

    if (!isAnalyticsEnabled) {
        sdk.isAnalyticsEnabled = false;
    }
    
    if ([autoCloseOnApprove isKindOfClass:NSNumber.class]) {
        [sdk setOnApproveDismissalTimeInterval:autoCloseOnApprove.doubleValue];
    }

    if (hasHandlers[@"onLog"]) {
        
        [sdk logHandler:^(SNSLogLevel level, NSString * _Nonnull message) {
            NSLog(@"[Idensic] %@", message);
            
            [weakSelf sendEventWithName:@"onLog" body:@{
                @"level": @(level),
                @"message": message ?: @"",
            }];
        }];
    }
    
    if (settings) {
        sdk.settings = settings;
    }

    if (strings) {
        sdk.strings = strings;
    }

    if (applicantConf[@"email"]) {
        sdk.initialEmail = applicantConf[@"email"];
    }
    if (applicantConf[@"phone"]) {
        sdk.initialPhone = applicantConf[@"phone"];
    }

    if (preferredDocumentDefinitions) {
        [sdk setPreferredDocumentDefinitionsFromJSON: preferredDocumentDefinitions];
    }

    [sdk setTokenExpirationHandler:^(void (^ _Nonnull onComplete)(NSString * _Nullable)) {
    
        weakSelf.tokenExpirationOnComplete = onComplete;
        
        [weakSelf sendEventWithName:@"onTokenExpired" body:nil];
    }];
    
    if (hasHandlers[@"onStatusChanged"]) {

        [sdk setOnStatusDidChange:^(SNSMobileSDK * _Nonnull sdk, SNSMobileSDKStatus prevStatus) {
            
            [weakSelf sendEventWithName:@"onStatusChanged" body:@{
                @"newStatus": [sdk descriptionForStatus:sdk.status] ?: @"",
                @"prevStatus": [sdk descriptionForStatus:prevStatus] ?: @"",
            }];
        }];
    }

    if (hasHandlers[@"onEvent"]) {
        
        [sdk onEvent:^(SNSMobileSDK * _Nonnull sdk, SNSEvent * _Nonnull event) {

            [weakSelf sendEventWithName:@"onEvent" body:@{
                @"eventType": [event descriptionForEventType:event.eventType] ?: @"",
                @"payload": event.payload ?: @{},
            }];
        }];
    }

    [sdk setOnDidDismiss:^(SNSMobileSDK * _Nonnull sdk) {
       
        sdk.logHandler = nil;
        sdk.tokenExpirationHandler = nil;
        sdk.onStatusDidChange = nil;
        sdk.onEvent = nil;

        resolve([weakSelf makeResult:sdk]);
    }];
    
    dispatch_async(dispatch_get_main_queue(), ^{

        if (theme) {
            sdk.theme = [SNSTheme fromJSON:theme assetsPath:@"assets/"];
        }
        
        [self applyCustomizationIfAny];
        
        UIViewController *topViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
        while (topViewController.presentedViewController) {
            topViewController = topViewController.presentedViewController;
        }

        [sdk presentFrom:topViewController];
    });
}

RCT_EXPORT_METHOD(updateAccessToken:(NSString *)newAccessToken)
{
    if (self.tokenExpirationOnComplete) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.tokenExpirationOnComplete(newAccessToken);
            self.tokenExpirationOnComplete = nil;
        });
    }
}

RCT_EXPORT_METHOD(dismiss)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.sdk dismiss];
    });
}

#pragma mark - Customization

/**
 * Usage:
 *
 * Add a class named `IdensicMobileSDKCustomization` into the main project
 * and define a static method named `apply:` that will take an instance of `SNSMobileSDK`
 *
 * For example, in Swift:
 *
 * import IdensicMobileSDK
 *
 * class IdensicMobileSDKCustomization: NSObject {
 *
 *   @objc static func apply(_ sdk: SNSMobileSDK) {
 *   }
 * }
 *
 */
- (void)applyCustomizationIfAny {
    
    NSString *className = @"IdensicMobileSDKCustomization";
    
    Class customization = [NSBundle.mainBundle classNamed:className];
    if (!customization) {
        NSString *classPrefix = [NSBundle.mainBundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleExecutableKey];
        if (classPrefix) {
            customization = [NSBundle.mainBundle classNamed:[NSString stringWithFormat:@"%@.%@", classPrefix, className]];
        }
    }
    
    if (customization && [customization respondsToSelector:@selector(apply:)]) {
        [customization performSelector:@selector(apply:) withObject:self.sdk];
    }
}

#pragma mark - Helpers

- (NSDictionary *)makeResult:(SNSMobileSDK *)sdk {
    
    NSMutableDictionary *result = NSMutableDictionary.new;
    
    result[@"success"] = @(sdk.status != SNSMobileSDKStatus_Failed);
    result[@"status"] = [sdk descriptionForStatus:sdk.status];
    
    if (sdk.status == SNSMobileSDKStatus_Failed) {
        result[@"errorType"] = [sdk descriptionForFailReason:sdk.failReason];
        result[@"errorMsg"] = sdk.verboseStatus;
    }
    
    return result.copy;
}

@end
