#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
#import <P101XP/P101XP-Swift.h>

#import "P101XP.h"
#import "P101XPHelper.h"
#import "P101XPDelegateWrapper.h"

extern "C"
{

static NSMutableDictionary* p101xpStaticPrefetchedProducts = [[NSMutableDictionary alloc] init];


// TODO: Move to the helper class
NSDictionary* getDictionary(const char *string)
{
    NSString *jsonString = [NSString stringWithUTF8String:string];
    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error = nil;
    return [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
}

// TODO: Move to the helper class
const char* cCopy(const char *string)
{
    if (string == NULL) {
        return NULL;
    }
    
    char *res = (char *)malloc(strlen(string) + 1);
    if (res == NULL) {
        return NULL;
    }
    strcpy(res, string);
    
    return res;
}


const char* NativeGetToken() {
    [P101XPHelper logWithMessage:@"MethodCall=NativeGetToken"];
    P101XPToken *token = [P101XPSDK getToken];
    if (token != nil) {
        NSString *result = [P101XPHelper jsonStringFrom:[P101XPHelper tokenToDict:token]];
        [P101XPHelper logWithPrefix:@"NativeGetToken_Json" withMessage:result];
        return cCopy([result cStringUsingEncoding:NSUTF8StringEncoding]);
    } else {
        [P101XPHelper logWithPrefix:@"NativeGetToken_Null" withMessage:@"Token is null!"];
        return nil;
    }
}


const char* NativeGetAccount() {
    [P101XPHelper logWithMessage:@"MethodCall=NativeGetAccount"];
    P101XPAccountData *account = [P101XPSDK getAccountData];
    if (account != nil) {
        NSString *result = [P101XPHelper jsonStringFrom:[P101XPHelper accountToDict:account]];
        [P101XPHelper logWithPrefix:@"NativeGetAccount_Json" withMessage:result];
        return cCopy([result cStringUsingEncoding:NSUTF8StringEncoding]);
    } else {
        [P101XPHelper logWithPrefix:@"NativeGetAccount_Null" withMessage:@"Account is null!"];
        return nil;
    }
}

const char* NativeGetProduct(const char *productId)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeGetProduct"];
    NSString *nsProductId = [NSString stringWithUTF8String:productId];
    P101XPProduct *product = [P101XPSDK getProduct:nsProductId];
    if (product != nil) {
        NSString *result = [P101XPHelper jsonStringFrom:[P101XPHelper productToDict:product]];
        if (result) {
            [P101XPHelper logWithPrefix:@"NativeGetProduct_Result" withMessage:result];
            return cCopy([result cStringUsingEncoding:NSUTF8StringEncoding]);
        } else {
            [P101XPHelper logWithPrefix:@"NativeGetProduct_Error" withMessage:@"Serialization result is nil."];
            return nil;
        }
    } else {
        [P101XPHelper logWithPrefix:@"NativeGetProduct_Error" withMessage:@"Product not found."];
        return nil;
    }
}

void NativeInitialize()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeInitialize"];
    [P101XPSDK initialize: [P101XPDelegateWrapper sharedWrapper]];
}

void NativeApplicationReview()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeApplicationReview"];
    [P101XPSDK applicationReview];
}

void NativeApplicationInvite()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeApplicationInvite"];
    [P101XPSDK applicationInvite];
}

void NativeAuthorize()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeAuthorize"];
    [P101XPSDK authorize];
}

void NativeGetSocialFriends()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeGetSocialFriends"];
    [P101XPSDK getSocialFriends:P101XPSNTypeSnFacebook];
}

void NativePrefetchProduct(const char *productId)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativePrefetchProduct"];
    NSString *nsProductId = [NSString stringWithUTF8String:productId];
    P101XPProduct *product = [P101XPSDK getProduct:nsProductId];
    if (product != nil) {
        [p101xpStaticPrefetchedProducts setObject:product forKey:nsProductId];
        [P101XPHelper logWithMessage:[NSString stringWithFormat:@"NativePrefetchProduct_Success:%@", nsProductId]];
    } else {
        [P101XPHelper logWithMessage:[NSString stringWithFormat:@"NativePrefetchProduct_Failure:%@", nsProductId]];
    }
}

void NativePurchaseProduct(const char *productId, const char *serverId, const char *properties)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativePurchaseProduct"];
    NSString *nsProductId = [NSString stringWithUTF8String:productId];
    NSString *nsServerId = [NSString stringWithUTF8String:serverId];
    NSDictionary *parsedDictionary = getDictionary(properties);
    P101XPProduct *product = [p101xpStaticPrefetchedProducts objectForKey:nsProductId];
    if (product == nil) {
        product = [P101XPSDK getProduct:nsProductId];
        if (product != nil) {
            [p101xpStaticPrefetchedProducts setObject:product forKey:nsProductId];
        }
    }
    if (product != nil) {
        [P101XPHelper logWithMessage:@"purchase product"];
        [P101XPSDK purchaseProduct:product serverId:nsServerId properties:parsedDictionary];
    } else {
        NSError *error = [P101XPHelper createErrorWithMessage:@"Product not found!"];
        [P101XPHelper logWithError:error];
        [[P101XPDelegateWrapper sharedWrapper] p101xpOnPurchaseProductWithResult:nil error:error];
    }
}

void NativeRestoreProducts()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeRestoreProducts"];
    [P101XPSDK restoreProducts];
}

void NativeGetProducts(const char *jsonProductIds)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeGetProducts"];
    if (jsonProductIds != nil) {
        NSDictionary *dictionary = getDictionary(jsonProductIds);
        if (dictionary == nil) {
            NSError *error = [P101XPHelper createErrorWithMessage:@"Invalid parameters!"];
            [[P101XPDelegateWrapper sharedWrapper] p101xpOnPurchaseProductWithResult:nil error:error];
        }
        [P101XPSDK getProducts:[dictionary allValues]];
    } else {
        [P101XPSDK getProducts:nil];
    }
}

void NativeFinishPurchase(const char *jsonPurchase)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeFinishPurchase"];
    [[P101XPDelegateWrapper sharedWrapper] onFinishPurchaseResult:jsonPurchase];
}

void NativeShowProfile()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeShowProfile"];
    [P101XPSDK showProfile];
}

void NativeLogEvent(const char *message)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeLogEvent"];
    [P101XPHelper logWithMessage:[NSString stringWithUTF8String:message]];
}

void NativeSendEventToAnalytic(const char *eventName, const char *eventProperties)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeSendEventToAnalytic"];
    NSString *name = [NSString stringWithUTF8String:eventName];
    NSDictionary *parsedDictionary = getDictionary(eventProperties);
    [P101XPSDK analyticsTrackWithEvent:name values:parsedDictionary];
}

void NativeShowMessage(const char *message)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeShowMessage"];
    NativeLogEvent(message);
    NSString *messageText = [NSString stringWithUTF8String:message];
    if (messageText == nil) {
        messageText = @"";
    }

    dispatch_async(dispatch_get_main_queue(), ^{
        UIWindow *presentingWindow = nil;
        if (@available(iOS 13.0, *)) {
            for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
                if (scene.activationState != UISceneActivationStateForegroundActive ||
                    ![scene isKindOfClass:[UIWindowScene class]]) {
                    continue;
                }

                for (UIWindow *window in ((UIWindowScene *)scene).windows) {
                    if (window.isKeyWindow) {
                        presentingWindow = window;
                        break;
                    }
                }

                if (presentingWindow != nil) {
                    break;
                }
            }
        }

        if (presentingWindow == nil) {
            presentingWindow = UIApplication.sharedApplication.keyWindow;
        }
        if (presentingWindow == nil && UIApplication.sharedApplication.windows.count > 0) {
            presentingWindow = UIApplication.sharedApplication.windows.firstObject;
        }

        UIViewController *presentingController = presentingWindow.rootViewController;
        while (presentingController.presentedViewController != nil) {
            presentingController = presentingController.presentedViewController;
        }
        
        if (presentingController == nil) {
            return;
        }

        UIAlertController *dialog =
            [UIAlertController alertControllerWithTitle:@"101XP"
                                                message:messageText
                                         preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *okAction =
            [UIAlertAction actionWithTitle:@"OK"
                                     style:UIAlertActionStyleDefault
                                   handler:nil];
        [dialog addAction:okAction];
        [presentingController presentViewController:dialog animated:YES completion:nil];
    });
}

void NativeForcedApplicationReview()
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeForcedApplicationReview"];
    [P101XPSDK forcedApplicationReview];
}

void NativeCrashLog(const char *tag, const char *text)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeCrashLog"];
    [P101XPSDK crashLog:[NSString stringWithUTF8String:tag]
                text:[NSString stringWithUTF8String:text]];
}

void NativeShowWebStore(const char *jsonPurchaseProperties)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeShowWebStore"];
    NSDictionary *parsedDictionary = getDictionary(jsonPurchaseProperties);
    [P101XPSDK showWebStoreWithProperties:parsedDictionary];
}

void NativeTrackSentryEvent(const char *event, const char *jsonValues)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeTrackSentryEvent"];
    NSString *objcEvent = [NSString stringWithUTF8String:event];
    NSDictionary *objcValues = getDictionary(jsonValues);
    [P101XPSDK trackSentryWithEvent:objcEvent values:objcValues];
}

void NativeTrackSentryError(const char *eventName, const char *message)
{
    [P101XPHelper logWithMessage:@"MethodCall=NativeTrackSentryError"];
    NSString *domain = @"com.p101xp.exception";
    NSString *objcEventName = [NSString stringWithUTF8String:eventName];
    NSString *objcMessage = [NSString stringWithUTF8String:message];
    NSDictionary *userInfo = @{
        NSLocalizedDescriptionKey: objcMessage ?: @"No error message provided!",
        NSLocalizedFailureReasonErrorKey: objcMessage ?: @"No error message provided!",
        @"ExceptionName": objcEventName ?: @"UnknownException"
    };
    NSError *objcError = [NSError errorWithDomain:domain code:-1 userInfo:userInfo];
    [P101XPSDK trackSentryWithError:objcError];
}

} /* extern "C" */
