#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>

@import SwrveSDK;

#import "SwrvePlugin.h"
#import "SwrvePluginPushHandler.h"
#import "SwrvePluginEventEmitter.h"

@interface SwrvePluginExampleTests : XCTestCase

@property(nonatomic, strong) Swrve *swrve;
@property(nonatomic, strong) SwrveMessageController *messaging;
@property(nonatomic, strong) SwrvePlugin *plugin;

@end

@interface SwrvePluginPushHandler (Tests)

- (void)resetStateForTesting;

@end

@interface SwrveSDK(SwrveTestAPI)

+ (void)addSharedInstance:(Swrve*)instance;
+ (void)resetSwrveSharedInstance;

@end

@interface SwrvePluginPushHandler ()

// Expose private method
- (void)handleNotificationUserInfo:(NSDictionary *)userInfo;

@end

@interface SwrvePlugin ()

// Expose private method
- (NSMutableDictionary *)getCache;

@end

@implementation SwrvePluginExampleTests

- (void)setUp {
    self.swrve = OCMClassMock([Swrve class]);

    self.plugin = OCMPartialMock([SwrvePlugin new]);
    self.plugin.swrveInstance = self.swrve;
}

- (void)tearDown {
  [SwrveSDK resetSwrveSharedInstance];
}

- (void)testStartWithUserId {

    [SwrveSDK addSharedInstance:self.swrve];

    [self.plugin startWithUserId:@"user-id"];

    OCMVerify([self.swrve startWithUserId:@"user-id"]);

    XCTestExpectation *expectation = [self expectationWithDescription:@"Wait a number of seconds to ensure the userUpdate is called"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [expectation fulfill];
    });
    [self waitForExpectationsWithTimeout:6 handler:^(NSError *error) {
    }];

    NSDictionary *userUpdates = @{
            @"swrve.react_native_version": @"0.73.4",
            @"swrve.react_native_plugin_version": @"6.0.0",
            @"swrve.react_native_new_arch_enabled": @"false"
    };
    OCMVerify([self.swrve userUpdate:userUpdates]);
}

- (void)testStopTracking {
    [self.plugin stopTracking];

    OCMVerify([self.swrve stopTracking]);
}

- (void)testStartWithoutUserId {
    [self.plugin startWithUserId:nil];

    OCMVerify([self.swrve start]);
}

- (void)testIdentifyInvalidId {
    [self.plugin identifyWithUserIdentity:@"" resolver:^(id result) {
        // Shouldn't get called
        XCTAssert(false);
    }                            rejecter:^(NSString *code, NSString *message, NSError *error) {
        // Should get called
        XCTAssertTrue(code.length > 0);
        XCTAssertTrue(message.length > 0);
        XCTAssertNotNil(error);
    }];
}

- (void)testIdentifyValidId {
    OCMStub([self.swrve start]);
    OCMStub([self.swrve identify:[OCMArg any]
                       onSuccess:[OCMArg invokeBlock]
                         onError:[OCMArg any]]);

    [self.plugin identifyWithUserIdentity:@"identity" resolver:^(id result) {
        // Couldn't get the 'invokeBlock' above to pass a value, which seems to be due to the success callback being typed 'id' (void*)
        // So just check it's the correct callback
        XCTAssert(true);
    }                            rejecter:^(NSString *code, NSString *message, NSError *error) {
        XCTAssert(false);
    }];
}

- (void)testIdentifyValidIdSwrveRejection {
    OCMStub([self.swrve start]);
    OCMStub([self.swrve identify:[OCMArg any]
                       onSuccess:[OCMArg any]
                         onError:[OCMArg invokeBlock]]);

    [self.plugin identifyWithUserIdentity:@"identity" resolver:^(id result) {

        XCTAssert(false);
    }                            rejecter:^(NSString *code, NSString *message, NSError *error) {
        XCTAssert(true);
    }];
}

/// Validate the dictionary from testUserUpdate
- (int)validateAttributes:(NSDictionary *)attributes {
    XCTAssert([[attributes objectForKey:@"string"] isEqualToString:@"string-value"]);
    XCTAssert([[attributes objectForKey:@"double"] isEqualToString:@"12.45"]);
    XCTAssert([[attributes objectForKey:@"int"] isEqualToString:@"1234"]);
    XCTAssert([[attributes objectForKey:@"boolean"] isEqualToString:@"1"]);
    return 0;
}

- (void)testUserUpdate {
    OCMStub([self.swrve userUpdate:[OCMArg any]]).andCall(self, @selector(validateAttributes:));

    // The Swrve API only supports NSDictionary<String,String> so we convert the non-string values to strings in the plugin
    // This is intended to help dynamic language developers use the API
    [self.plugin userUpdateWithattributes:@{
            @"string": @"string-value",
            @"boolean": @true,
            @"int": @1234,
            @"double": @12.45
    }];

    OCMVerify([self.swrve userUpdate:[OCMArg any]]);
}

- (void)testuserUpdateWithDate {
    NSString *dateString = @"2020-04-17T06:45:56.645Z";
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
    NSLocale *posix = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
    [formatter setLocale:posix];
    NSDate *date = [formatter dateFromString:dateString];

    // Check that the date coming through to Swrve is the same as that above
    OCMStub([self.swrve userUpdate:[OCMArg isKindOfClass:NSString.class] withDate:[OCMArg checkWithBlock:^BOOL(id obj) {
        return [(NSDate *) obj isEqualToDate:date];
    }]]);

    [self.plugin userUpdateWithName:@"name" date:dateString];
}

- (void)testEventWithPayload {
    OCMStub([self.swrve event:[OCMArg any] payload:[OCMArg checkWithBlock:^BOOL(id obj) {
        [self validateAttributes:obj];
        return true;
    }]]);

    // The Swrve API only supports NSDictionary<String,String> so we convert the non-string values to strings in the plugin
    // This is intended to help dynamic language developers use the API
    [self.plugin eventWithName:@"event" eventPayload:@{
            @"string": @"string-value",
            @"boolean": @true,
            @"int": @1234,
            @"double": @12.45
    }];

    OCMVerify([self.swrve event:@"event" payload:[OCMArg any]]);
}

- (void)testSendQueuedEvents {
    [self.plugin sendQueuedEvents];

    OCMVerify([self.swrve sendQueuedEvents]);
}

- (void)testCurrencyGiven {
    [self.plugin currencyGivenWithCurrency:@"GBP" quantity:12];

    OCMVerify([self.swrve currencyGiven:@"GBP" givenAmount:12]);
}

- (void)testPurchase {
    [self.plugin purchaseItemWithName:@"item-name" currency:@"GBP" quantity:12 cost:22];

    OCMVerify([self.swrve purchaseItem:@"item-name" currency:@"GBP" cost:22 quantity:12]);
}

- (void)testUnvalidatedIap {
    [self.plugin unvalidatedIapWithlocalCost:23.4 localCurrency:@"Bob" productId:@"product-id" quantity:12];

    OCMVerify([self.swrve unvalidatedIap:nil localCost:23.4 localCurrency:@"Bob" productId:@"product-id" productIdQuantity:12]);
}

- (bool)checkSwrveRewards:(SwrveIAPRewards *)swrveRewards {
    NSDictionary *rewards = [(SwrveIAPRewards *) swrveRewards rewards];
    XCTAssert([[[rewards objectForKey:@"Pieces of eight"] objectForKey:@"amount"] isEqual:@33]);
    XCTAssert([[[rewards objectForKey:@"hoodie"] objectForKey:@"type"] isEqual:@"item"]);
    XCTAssert([[[rewards objectForKey:@"sword"] objectForKey:@"amount"] isEqual:@34]);
    return true;
}

- (void)testUnvalidatedIapWithReward {
    NSDictionary *sentRwards = @{
            @"items": @[
                    @{
                            @"name": @"hoodie",
                            @"amount": @13
                    },
                    @{
                            @"name": @"sword",
                            @"amount": @34
                    }
            ],
            @"currencies": @[
                    @{
                            @"name": @"Pieces of eight",
                            @"amount": @33
                    }
            ]
    };

    [self.plugin unvalidatedIapWithlocalCost:23 localCurrency:@"Gold" productId:@"product-id" quantity:12 rewardMap:sentRwards];

    OCMVerify([self.swrve unvalidatedIap:[OCMArg checkWithSelector:@selector(checkSwrveRewards:) onObject:self] localCost:23 localCurrency:@"Gold" productId:@"product-id" productIdQuantity:12]);
}

- (void)testGetApiKey {

    [self.plugin getApiKeyWithResolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                         rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve apiKey]);
}

- (void)testGetUserId {

    [self.plugin getUserIdWithResolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                         rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve userID]);
}

- (void)testGetExternalUserId {

    [self.plugin getExternalUserIdWithResolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                                 rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve externalUserId]);
}

- (void)testIsStarted {

    [self.plugin isStartedWithResolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                         rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve started]);
}

- (void)testGetUserResources {

    [self.plugin getUserResourcesWithResolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                                rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve userResources:[OCMArg any]]);
}

- (void)testGetUserResourcesDiff {

    [self.plugin getUserResourcesDiffWithResolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                                    rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve userResourcesDiffWithListener:[OCMArg any]]);
}

- (void)testGetRealTimeUserProperties {

    [self.plugin getRealTimeUserPropertiesWithResolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                                         rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve realTimeUserProperties:[OCMArg any]]);
}

- (void)testRefreshResourcesAndCampaigns {

    [self.plugin refreshCampaignsAndResources];

    OCMVerify([self.swrve refreshCampaignsAndResources]);
}

- (void)testGetMessageCenterCampaigns {
    SwrveCampaign *campaignMock = OCMClassMock([SwrveCampaign class]);
    SwrveCampaignState *campaignStateMock = OCMClassMock([SwrveCampaignState class]);
    SwrveMessageCenterDetails *messageCenterDetailsMock = OCMClassMock([SwrveMessageCenterDetails class]);

    // Mock Campaign State
    OCMStub([campaignStateMock campaignID]).andReturn(44);
    OCMStub([campaignStateMock status]).andReturn(SwrveCampaignStatusUnseen);
    OCMStub([campaignStateMock impressions]).andReturn(0);

    // Mock Campaign Message Center Details
    OCMStub([messageCenterDetailsMock subject]).andReturn(@"IAM message center subject");
//  OCMStub([messageCenterDetailsMock description]).andReturn(@"IAM message center description");
    OCMStub([messageCenterDetailsMock imageAccessibilityText]).andReturn(@"IAM message center image accesibility text");
    OCMStub([messageCenterDetailsMock imageUrl]).andReturn(@"https://faker.image.com/");
    OCMStub([messageCenterDetailsMock imageSha]).andReturn(@"0590479d0050002e99f411f72d0d635351134a12");

    // Mock Campaign
    OCMStub([campaignMock ID]).andReturn(44);
    OCMStub([campaignMock name]).andReturn(@"IAM name");
    OCMStub([campaignMock messageCenter]).andReturn(true);
    OCMStub([campaignMock maxImpressions]).andReturn(11111);
    OCMStub([campaignMock priority]).andReturn([NSNumber numberWithInt:(9999)]);
    OCMStub([campaignMock downloadDate]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock dateStart]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock dateEnd]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock messageCenterDetails]).andReturn(messageCenterDetailsMock);
    OCMStub([campaignStateMock status]).andReturn(campaignStateMock);

    // Mock Campaigns List
    NSArray *mockList = [NSArray arrayWithObject:campaignMock];
    OCMExpect([self.swrve messageCenterCampaigns]).andReturn(mockList);

    XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];

    [self.plugin getMessageCenterCampaignsWithPersonalization:nil resolver:^(id result) {

        XCTAssertNotNil(result, @"result should not be null");
        NSArray *messageCentre = (NSArray *) result;

        NSDictionary *firstCampaign = [messageCentre firstObject];
        XCTAssertNotNil(firstCampaign, @"Campaign from Message Center should not be null");
        XCTAssertEqualObjects([firstCampaign objectForKey:@"name"], @"IAM name");
        XCTAssertEqual([[firstCampaign objectForKey:@"ID"] integerValue], 44);
        XCTAssertTrue([firstCampaign objectForKey:@"messageCenter"], @"messageCenter should be true");
        XCTAssertEqual([[firstCampaign objectForKey:@"priority"] integerValue], 9999);
        XCTAssertEqual([[firstCampaign objectForKey:@"maxImpressions"] integerValue], 11111);
        XCTAssertEqual([[firstCampaign objectForKey:@"downloadDate"] integerValue], 1362671700);
        XCTAssertEqual([[firstCampaign objectForKey:@"dateStart"] integerValue], 1362671700);
        XCTAssertEqual([[firstCampaign objectForKey:@"dateEnd"] integerValue], 1362671700);

        NSDictionary *firstCampaignState = [firstCampaign objectForKey:@"state"];
        XCTAssertEqual([[firstCampaignState objectForKey:@"next"] integerValue], 0);
        XCTAssertEqualObjects([firstCampaignState objectForKey:@"status"], @"Unseen");
        XCTAssertEqual([[firstCampaignState objectForKey:@"impressions"] integerValue], 0);

        NSDictionary *firstCampaignMessageCenterDetails = [firstCampaign objectForKey:@"messageCenterDetails"];
        XCTAssertEqualObjects([firstCampaignMessageCenterDetails objectForKey:@"subject"], @"IAM message center subject");
//    XCTAssertEqualObjects([firstCampaignMessageCenterDetails objectForKey:@"description"], @"IAM message center description");
        XCTAssertEqualObjects([firstCampaignMessageCenterDetails objectForKey:@"imageAccessibilityText"], @"IAM message center image accesibility text");
        XCTAssertEqualObjects([firstCampaignMessageCenterDetails objectForKey:@"imageURL"], @"https://faker.image.com/");
        XCTAssertEqualObjects([firstCampaignMessageCenterDetails objectForKey:@"imageSha"], @"0590479d0050002e99f411f72d0d635351134a12");

        [promiseResolved fulfill];

    }                                                rejecter:^(NSString *code, NSString *message, NSError *error) {
        XCTFail(@"Rejected: this should not be rejected");
    }];

    // waiting for resolver to complete
    [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
        if (error) {
            XCTFail(@"Ran out of time: GetMessageCenter");
        }
    }];

    OCMVerify([self.swrve messageCenterCampaigns]);
}

- (void)testGetMessageCenterCampaign {
    SwrveCampaign *campaignMock = OCMClassMock([SwrveCampaign class]);
    SwrveCampaignState *campaignStateMock = OCMClassMock([SwrveCampaignState class]);
    SwrveMessageCenterDetails *messageCenterDetailsMock = OCMClassMock([SwrveMessageCenterDetails class]);

    // Mock Campaign State
    OCMStub([campaignStateMock campaignID]).andReturn(44);
    OCMStub([campaignStateMock status]).andReturn(SwrveCampaignStatusUnseen);
    OCMStub([campaignStateMock impressions]).andReturn(0);

    // Mock Campaign Message Center Details
    OCMStub([messageCenterDetailsMock subject]).andReturn(@"IAM message center subject");
//  OCMStub([messageCenterDetailsMock description]).andReturn(@"IAM message center description");
    OCMStub([messageCenterDetailsMock imageAccessibilityText]).andReturn(@"IAM message center image accesibility text");
    NSString *imageURL = @"https://faker.image.com/dummyimage.png";
    OCMStub([messageCenterDetailsMock imageUrl]).andReturn(imageURL);
    NSData *imageURLData = [imageURL dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
    NSString *imageURLSha = [SwrveUtils sha1:imageURLData];
    [SwrvePluginExampleTests createDummyAsset:imageURLSha];
    OCMStub([messageCenterDetailsMock imageSha]).andReturn(@"0590479d0050002e99f411f72d0d635351134a12");

    // Mock Campaign
    OCMStub([campaignMock ID]).andReturn(44);
    OCMStub([campaignMock name]).andReturn(@"IAM name");
    OCMStub([campaignMock messageCenter]).andReturn(true);
    OCMStub([campaignMock maxImpressions]).andReturn(11111);
    OCMStub([campaignMock priority]).andReturn([NSNumber numberWithInt:(9999)]);
    OCMStub([campaignMock downloadDate]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock dateStart]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock dateEnd]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock messageCenterDetails]).andReturn(messageCenterDetailsMock);
    OCMStub([campaignStateMock status]).andReturn(campaignStateMock);

    OCMExpect([self.swrve messageCenterCampaignWithID:44 andPersonalization:nil]).andReturn(campaignMock);

    XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];

    [self.plugin getMessageCenterCampaignWithId:44 andPersonalization:nil resolver:^(id result) {

        XCTAssertNotNil(result, @"result should not be null");

        NSDictionary *campaign = result;
        XCTAssertNotNil(campaign, @"Campaign from Message Center should not be null");
        XCTAssertEqualObjects([campaign objectForKey:@"name"], @"IAM name");
        XCTAssertEqual([[campaign objectForKey:@"ID"] integerValue], 44);
        XCTAssertTrue([campaign objectForKey:@"messageCenter"], @"messageCenter should be true");
        XCTAssertEqual([[campaign objectForKey:@"maxImpressions"] integerValue], 11111);
        XCTAssertEqual([[campaign objectForKey:@"downloadDate"] integerValue], 1362671700);
        XCTAssertEqual([[campaign objectForKey:@"dateStart"] integerValue], 1362671700);
        XCTAssertEqual([[campaign objectForKey:@"dateEnd"] integerValue], 1362671700);

        NSDictionary *campaignState = [campaign objectForKey:@"state"];
        XCTAssertEqual([[campaignState objectForKey:@"next"] integerValue], 0);
        XCTAssertEqualObjects([campaignState objectForKey:@"status"], @"Unseen");
        XCTAssertEqual([[campaignState objectForKey:@"impressions"] integerValue], 0);

        NSDictionary *campaignMessageCenterDetails = [campaign objectForKey:@"messageCenterDetails"];
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"subject"], @"IAM message center subject");
//    XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"description"], @"IAM message center description");
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"imageAccessibilityText"], @"IAM message center image accesibility text");
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"imageURL"], @"https://faker.image.com/dummyimage.png");
        NSString *cacheFolder = [SwrvePluginExampleTests campaignCacheDirectory];
        NSString *assetFilePath = [cacheFolder stringByAppendingPathComponent:imageURLSha];
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"image"], assetFilePath);
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"imageSha"], @"0590479d0050002e99f411f72d0d635351134a12");

        [SwrvePluginExampleTests removeDummyAsset:imageURLSha];
        [promiseResolved fulfill];

    }                                  rejecter:^(NSString *code, NSString *message, NSError *error) {
        XCTFail(@"Rejected: this should not be rejected");
    }];

    // waiting for resolver to complete
    [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
        if (error) {
            XCTFail(@"Ran out of time: GetMessageCenter");
        }
    }];

    OCMVerify([self.swrve messageCenterCampaignWithID:44 andPersonalization:nil]);
}

- (void)testGetMessageCenterCampaignUsingFallback {
    SwrveCampaign *campaignMock = OCMClassMock([SwrveCampaign class]);
    SwrveCampaignState *campaignStateMock = OCMClassMock([SwrveCampaignState class]);
    SwrveMessageCenterDetails *messageCenterDetailsMock = OCMClassMock([SwrveMessageCenterDetails class]);

    // Mock Campaign Message Center Details
    OCMStub([messageCenterDetailsMock imageUrl]).andReturn(@"https://faker.image.com/dummyimage.png");
    NSString *imageSha = @"0590479d0050002e99f411f72d0d635351134a12";
    OCMStub([messageCenterDetailsMock imageSha]).andReturn(imageSha);
    [SwrvePluginExampleTests createDummyAsset:imageSha]; // create dummy asset using the fallback instead of the url

    // Mock Campaign
    OCMStub([campaignMock ID]).andReturn(44);
    OCMStub([campaignMock name]).andReturn(@"IAM name");
    OCMStub([campaignMock messageCenter]).andReturn(true);
    OCMStub([campaignMock maxImpressions]).andReturn(11111);
    OCMStub([campaignMock priority]).andReturn([NSNumber numberWithInt:(9999)]);
    OCMStub([campaignMock downloadDate]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock dateStart]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock dateEnd]).andReturn([NSDate dateWithTimeIntervalSince1970:1362671700]);
    OCMStub([campaignMock messageCenterDetails]).andReturn(messageCenterDetailsMock);
    OCMStub([campaignStateMock status]).andReturn(campaignStateMock);

    OCMExpect([self.swrve messageCenterCampaignWithID:44 andPersonalization:nil]).andReturn(campaignMock);

    XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];

    [self.plugin getMessageCenterCampaignWithId:44 andPersonalization:nil resolver:^(id result) {

        XCTAssertNotNil(result, @"result should not be null");

        NSDictionary *campaign = result;
        NSDictionary *campaignMessageCenterDetails = [campaign objectForKey:@"messageCenterDetails"];
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"imageURL"], @"https://faker.image.com/dummyimage.png");
        NSString *cacheFolder = [SwrvePluginExampleTests campaignCacheDirectory];
        NSString *assetFilePath = [cacheFolder stringByAppendingPathComponent:imageSha];
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"image"], assetFilePath); // verify that the fallback is the file path
        XCTAssertEqualObjects([campaignMessageCenterDetails objectForKey:@"imageSha"], @"0590479d0050002e99f411f72d0d635351134a12");

        [SwrvePluginExampleTests removeDummyAsset:imageSha];
        [promiseResolved fulfill];

    }                                  rejecter:^(NSString *code, NSString *message, NSError *error) {
        XCTFail(@"Rejected: this should not be rejected");
    }];

    // waiting for resolver to complete
    [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
        if (error) {
            XCTFail(@"Ran out of time: GetMessageCenter");
        }
    }];

    OCMVerify([self.swrve messageCenterCampaignWithID:44 andPersonalization:nil]);
}


- (void)testShowMessageCenterCampaign {
    // Mock campaign response since we search by number
    SwrveCampaign *campaignMock = OCMClassMock([SwrveCampaign class]);
    OCMStub([campaignMock ID]).andReturn(88);

    NSArray *mockList = [NSArray arrayWithObject:campaignMock];
    OCMExpect([self.swrve messageCenterCampaigns]).andReturn(mockList);

    [self.plugin showMessageCenterCampaignWithId:88 withPersonalization:nil];
    OCMVerify([self.swrve messageCenterCampaigns]);
    OCMVerify([self.swrve showMessageCenterCampaign:campaignMock withPersonalization:nil]);

}

- (void)testRemoveMessageCenterCampaign {
    // Mock campaign response since we search by number
    SwrveCampaign *campaignMock = OCMClassMock([SwrveCampaign class]);
    OCMStub([campaignMock ID]).andReturn(44);

    NSArray *mockList = [NSArray arrayWithObject:campaignMock];
    OCMExpect([self.swrve messageCenterCampaigns]).andReturn(mockList);

    [self.plugin removeMessageCenterCampaignWithId:44];

    OCMVerify([self.swrve messageCenterCampaigns]);
    OCMVerify([self.swrve removeMessageCenterCampaign:campaignMock]);
}

- (void)testMarkMessageCenterCampaignAsSeen {
    SwrveCampaign *campaignMock = OCMClassMock([SwrveCampaign class]);
    OCMStub([campaignMock ID]).andReturn(55);

    NSArray *mockList = [NSArray arrayWithObject:campaignMock];
    OCMExpect([self.swrve messageCenterCampaigns]).andReturn(mockList);

    [self.plugin markMessageCenterCampaignAsSeenWithId:55];

    OCMVerify([self.swrve messageCenterCampaigns]);
    OCMVerify([self.swrve markMessageCenterCampaignAsSeen:campaignMock]);
}

- (void)testPushBufferingWontSendYet {
    [SwrvePluginPushHandler.sharedInstance resetStateForTesting];

    id observerMock = OCMObserverMock();
    [NSNotificationCenter.defaultCenter addMockObserver:observerMock name:PUSH_EVENT_NAME object:nil];

    [SwrvePlugin initWithAppID:123 apiKey:@"345" config:nil];

    [SwrvePluginPushHandler.sharedInstance handleNotificationUserInfo:@{@"key": @"value"}];

    // No expectations so should be fine
    OCMVerifyAllWithDelay((OCMockObject *) self.swrve, 1.0);
    OCMVerifyAll(observerMock);
}

- (void)testPushBufferingSendsWhenAttached {
    [SwrvePluginPushHandler.sharedInstance resetStateForTesting];

    id observerMock = OCMObserverMock();
    [NSNotificationCenter.defaultCenter addMockObserver:observerMock name:PUSH_EVENT_NAME object:nil];
    [[observerMock expect] notificationWithName:PUSH_EVENT_NAME object:[OCMArg any] userInfo:[OCMArg any]];
    [SwrvePlugin initWithAppID:123 apiKey:@"345" config:nil];

    [SwrvePluginPushHandler.sharedInstance handleNotificationUserInfo:@{@"key": @"value"}];
    [self.plugin startedListening];

    // Wait a little before verifying
    OCMVerifyAllWithDelay((OCMockObject *) self.swrve, 1.0);
    OCMVerifyAll(observerMock);
}

- (void)testEmbeddedBufferingWontSendYet {
    [SwrvePluginPushHandler.sharedInstance resetStateForTesting];

    id observerMock = OCMObserverMock();
    [NSNotificationCenter.defaultCenter addMockObserver:observerMock name:EMBEDDED_CALLBACK_EVENT_NAME object:nil];

    [SwrvePlugin initWithAppID:123 apiKey:@"345" config:nil];
    SwrveEmbeddedMessage *embeddedMock = OCMClassMock([SwrveEmbeddedMessage class]);
    [SwrveSDK config].embeddedMessageConfig.embeddedCallback(embeddedMock, [NSDictionary new], YES);

    // No expectations so should be fine
    OCMVerifyAllWithDelay((OCMockObject *) self.swrve, 1.0);
    OCMVerifyAll(observerMock);
}

- (void)testEmbeddedBufferingSendsWhenAttached {
    [SwrvePluginPushHandler.sharedInstance resetStateForTesting];

    id observerMock = OCMObserverMock();
    [NSNotificationCenter.defaultCenter addMockObserver:observerMock name:EMBEDDED_CALLBACK_EVENT_NAME object:nil];
    [[observerMock expect] notificationWithName:EMBEDDED_CALLBACK_EVENT_NAME object:[OCMArg any] userInfo:[OCMArg any]];

    [SwrvePlugin initWithAppID:123 apiKey:@"345" config:nil];
    SwrveEmbeddedMessage *embeddedMock = OCMClassMock([SwrveEmbeddedMessage class]);
    [SwrveSDK config].embeddedMessageConfig.embeddedCallback(embeddedMock, [NSDictionary new], YES);
    [self.plugin startedEmbeddedListening];

    // Wait a little before verifying
    OCMVerifyAllWithDelay((OCMockObject *) self.swrve, 1.0);
    OCMVerifyAll(observerMock);
}

- (void)testEmbeddedMessageWasShownToUser {
    NSDictionary *cache = @{
            @"campaigns": @[
                    @{
                            @"id": @55,
                            @"start_date": @1630510173000,
                            @"end_date": @2145920400000,
                            @"rules": @{
                            @"delay_first_message": @180,
                            @"dismiss_after_views": @99999,
                            @"display_order": @"random",
                            @"min_delay_between_messages": @60
                    },
                            @"message_center": @true,
                            @"embedded_message": @{
                            @"id": @5,
                            @"name": @"Test message",
                            @"data": @"Hello test message",
                            @"type": @"other",
                            @"rules": @{},
                            @"priority": @9999,
                            @"subject": @""
                    }
                    }
            ]
    };
    OCMStub([self.plugin getCache]).andReturn([cache mutableCopy]);

    [self.plugin markEmbeddedMessageCampaignAsSeenWithId:55];

    OCMVerify([self.swrve embeddedMessageWasShownToUser:[OCMArg isKindOfClass:[SwrveEmbeddedMessage class]]]);
}

- (void)testEmbeddedControlMessageImpressionEvent {
    NSDictionary *cache = @{
            @"campaigns": @[
                    @{
                            @"id": @55,
                            @"start_date": @1630510173000,
                            @"end_date": @2145920400000,
                            @"rules": @{
                            @"delay_first_message": @180,
                            @"dismiss_after_views": @99999,
                            @"display_order": @"random",
                            @"min_delay_between_messages": @60
                    },
                            @"message_center": @true,
                            @"embedded_message": @{
                            @"id": @5,
                            @"name": @"Test message",
                            @"data": @"Hello test message",
                            @"type": @"other",
                            @"rules": @{},
                            @"priority": @9999,
                            @"subject": @""
                    }
                    }
            ]
    };
    OCMStub([self.plugin getCache]).andReturn([cache mutableCopy]);

    [self.plugin embeddedControlMessageImpressionEventWithId:55];

    OCMVerify([self.swrve embeddedControlMessageImpressionEvent:[OCMArg isKindOfClass:[SwrveEmbeddedMessage class]]]);
}

- (void)testMarkEmbeddedMessageButtonAsPressed {
    NSDictionary *cache = @{
            @"campaigns": @[
                    @{
                            @"id": @55,
                            @"start_date": @1630510173000,
                            @"end_date": @2145920400000,
                            @"rules": @{
                            @"delay_first_message": @180,
                            @"dismiss_after_views": @99999,
                            @"display_order": @"random",
                            @"min_delay_between_messages": @60
                    },
                            @"message_center": @true,
                            @"embedded_message": @{
                            @"id": @5,
                            @"name": @"Test message",
                            @"data": @"Hello test message",
                            @"type": @"other",
                            @"rules": @{},
                            @"priority": @9999,
                            @"subject": @""
                    }
                    }
            ]
    };
    OCMStub([self.plugin getCache]).andReturn([cache mutableCopy]);

    [self.plugin markEmbeddedMessageButtonAsPressedWithId:55 forButton:@"Button one"];

    OCMVerify([self.swrve embeddedButtonWasPressed:[OCMArg isKindOfClass:[SwrveEmbeddedMessage class]] buttonName:[OCMArg isEqual:(@"Button one")]]);
}

- (void)testGetPersonalizedText {
    [self.plugin getPersonalizedTextWithText:@"test string" andPersonalization:@{@"key": @"value"} resolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                               rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve personalizeText:[OCMArg any] withPersonalization:[OCMArg any]]);
}

- (void)testGetPersonalizedEmbeddedMessageData {
    NSDictionary *cache = @{
            @"campaigns": @[
                    @{
                            @"id": @55,
                            @"start_date": @1630510173000,
                            @"end_date": @2145920400000,
                            @"rules": @{
                            @"delay_first_message": @180,
                            @"dismiss_after_views": @99999,
                            @"display_order": @"random",
                            @"min_delay_between_messages": @60
                    },
                            @"message_center": @true,
                            @"embedded_message": @{
                            @"id": @5,
                            @"name": @"Test message",
                            @"data": @"Hello test message",
                            @"type": @"other",
                            @"rules": @{},
                            @"priority": @9999,
                            @"subject": @""
                    }
                    }
            ]
    };
    OCMStub([self.plugin getCache]).andReturn([cache mutableCopy]);

    [self.plugin getPersonalizedEmbeddedMessageDataWithId:55 andPersonalization:@{@"key": @"value"} resolver:^(id result) {
        // doesn't have to do anything we are verifying it was called.
    }                                            rejecter:^(NSString *code, NSString *message, NSError *error) {

    }];

    OCMVerify([self.swrve personalizeEmbeddedMessageData:[OCMArg any] withPersonalization:[OCMArg any]]);
}

- (void)testGetPushInboxMessageCampaigns {
  SwrvePushInboxMessage *pushInboxMessageMock = OCMClassMock([SwrvePushInboxMessage class]);
  OCMStub([pushInboxMessageMock messageId]).andReturn(3614565938492356600);
  OCMStub([pushInboxMessageMock variantId]).andReturn(3614565938492356601);
  OCMStub([pushInboxMessageMock endDate]).andReturn(2145920400000);
  OCMStub([pushInboxMessageMock sentDate]).andReturn(1663151262000);
  NSMutableDictionary *cjDictionary = [[NSMutableDictionary alloc] init];
  [cjDictionary setValue:@"bye" forKey:@"hello"];
  OCMStub([pushInboxMessageMock customerJson]).andReturn(cjDictionary);
  OCMStub([pushInboxMessageMock state]).andReturn(SwrvePushInboxMessageStateUNREAD);
  
  NSArray *mockList = [NSArray arrayWithObject:pushInboxMessageMock];
  OCMExpect([self.swrve pushInboxMessages]).andReturn(mockList);
  
  XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];
  
  [self.plugin getPushInboxMessagesWithResolver:^(id result) {
    
    XCTAssertNotNil(result, @"result should not be null");
    NSArray *pushInboxMessages = (NSArray *) result;
    
    NSDictionary *pushInboxMessage = [pushInboxMessages firstObject];
    XCTAssertNotNil(pushInboxMessage, @"pushInboxMessage should not be null");
    XCTAssertEqualObjects([pushInboxMessage objectForKey:@"messageId"], @"3614565938492356600");
    XCTAssertEqualObjects([pushInboxMessage objectForKey:@"variantId"], @"3614565938492356601");
    XCTAssertEqual([[pushInboxMessage objectForKey:@"endDate"] integerValue], 2145920400000);
    XCTAssertEqual([[pushInboxMessage objectForKey:@"sentDate"] integerValue], 1663151262000);
    NSDictionary *customerJson = [pushInboxMessage objectForKey:@"customerJson"];
    XCTAssertEqualObjects([customerJson objectForKey:@"hello"], @"bye");
    XCTAssertEqualObjects([pushInboxMessage objectForKey:@"state"], @"UNREAD");
    
    [promiseResolved fulfill];
  }                                                rejecter:^(NSString *code, NSString *message, NSError *error) {
    XCTFail(@"Rejected: this should not be rejected");
  }];
  
  // waiting for resolver to complete
  [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
    if (error) {
      XCTFail(@"Ran out of time: getPushInboxMessages");
    }
  }];
  
  OCMVerify([self.swrve pushInboxMessages]);
}

- (void)testEngagePushInboxMessageSuccess {
  
  UInt64 knownMessageId = 3614565938492356600;
  SwrvePushInboxResult *knownResult = [[SwrvePushInboxResult alloc] init:SwrvePushInboxResultCodeSUCCESS :@"" :200];
  OCMExpect([self.swrve engagePushInboxMessage:knownMessageId listener:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
      // Extract the listener argument from the invocation
      __unsafe_unretained id<SwrvePushInboxDelegate> delegate;
      [invocation getArgument:&delegate atIndex:3]; // The listener is the 3rd argument (0 is self, 1 is the selector, 2 is messageId, 3 is listener)
      [delegate onComplete:knownMessageId result:knownResult]; // Simulate the delegate's onComplete method being called
  });

  XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];
  
  NSString *knownMessageIdString = [[NSNumber numberWithUnsignedLongLong:knownMessageId] stringValue];
  [self.plugin engagePushInboxMessageWithMessageId:knownMessageIdString
                                          resolver:^(id result) {
    XCTAssertNotNil(result, @"result should not be null");
    
    NSDictionary *resultDictionary = result;
    XCTAssertNotNil(resultDictionary, @"Result should not be null");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"messageId"], knownMessageIdString);
    XCTAssertEqualObjects([resultDictionary objectForKey:@"resultCode"], @"SUCCESS");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"errorMessage"], @"");
    XCTAssertEqual([[resultDictionary objectForKey:@"httpResponseCode"] integerValue], 200);

    [promiseResolved fulfill];
  }
                                          rejecter:^(NSString *code, NSString *message, NSError *error) {
    XCTFail(@"Rejected: this should not be rejected");
  }];
  
  // waiting for resolver to complete
  [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
    if (error) {
      XCTFail(@"Ran out of time: engagePushInboxMessageWithMessageId");
    }
  }];
  
  OCMVerify([self.swrve engagePushInboxMessage:knownMessageId listener:OCMOCK_ANY]);
}

- (void)testEngagePushInboxMessageError {
  
  UInt64 knownMessageId = 3614565938492356600;
  SwrvePushInboxResult *knownResult = [[SwrvePushInboxResult alloc] init:SwrvePushInboxResultCodeERROR :@"Server Error" :500];
  OCMExpect([self.swrve engagePushInboxMessage:knownMessageId listener:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
      // Extract the listener argument from the invocation
      __unsafe_unretained id<SwrvePushInboxDelegate> delegate;
      [invocation getArgument:&delegate atIndex:3]; // The listener is the 3rd argument (0 is self, 1 is the selector, 2 is messageId, 3 is listener)
      [delegate onComplete:knownMessageId result:knownResult]; // Simulate the delegate's onComplete method being called
  });

  XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];
  
  NSString *knownMessageIdString = [[NSNumber numberWithUnsignedLongLong:knownMessageId] stringValue];
  [self.plugin engagePushInboxMessageWithMessageId:knownMessageIdString
                                          resolver:^(id result) {
    XCTAssertNotNil(result, @"result should not be null");
    
    NSDictionary *resultDictionary = result;
    XCTAssertNotNil(resultDictionary, @"Result should not be null");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"messageId"], knownMessageIdString);
    XCTAssertEqualObjects([resultDictionary objectForKey:@"resultCode"], @"ERROR");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"errorMessage"], @"Server Error");
    XCTAssertEqual([[resultDictionary objectForKey:@"httpResponseCode"] integerValue], 500);

    [promiseResolved fulfill];
  }
                                          rejecter:^(NSString *code, NSString *message, NSError *error) {
    XCTFail(@"Rejected: this should not be rejected");
  }];
  
  // waiting for resolver to complete
  [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
    if (error) {
      XCTFail(@"Ran out of time: engagePushInboxMessageWithMessageId");
    }
  }];
  
  OCMVerify([self.swrve engagePushInboxMessage:knownMessageId listener:OCMOCK_ANY]);
}

- (void)testReadPushInboxMessageSuccess {
  
  UInt64 knownMessageId = 3614565938492356600;
  SwrvePushInboxResult *knownResult = [[SwrvePushInboxResult alloc] init:SwrvePushInboxResultCodeSUCCESS :@"" :200];
  OCMExpect([self.swrve readPushInboxMessage:knownMessageId listener:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
      // Extract the listener argument from the invocation
      __unsafe_unretained id<SwrvePushInboxDelegate> delegate;
      [invocation getArgument:&delegate atIndex:3]; // The listener is the 3rd argument (0 is self, 1 is the selector, 2 is messageId, 3 is listener)
      [delegate onComplete:knownMessageId result:knownResult]; // Simulate the delegate's onComplete method being called
  });

  XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];
  
  NSString *knownMessageIdString = [[NSNumber numberWithUnsignedLongLong:knownMessageId] stringValue];
  [self.plugin readPushInboxMessageWithMessageId:knownMessageIdString
                                          resolver:^(id result) {
    XCTAssertNotNil(result, @"result should not be null");
    
    NSDictionary *resultDictionary = result;
    XCTAssertNotNil(resultDictionary, @"Result should not be null");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"messageId"], knownMessageIdString);
    XCTAssertEqualObjects([resultDictionary objectForKey:@"resultCode"], @"SUCCESS");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"errorMessage"], @"");
    XCTAssertEqual([[resultDictionary objectForKey:@"httpResponseCode"] integerValue], 200);

    [promiseResolved fulfill];
  }
                                          rejecter:^(NSString *code, NSString *message, NSError *error) {
    XCTFail(@"Rejected: this should not be rejected");
  }];
  
  // waiting for resolver to complete
  [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
    if (error) {
      XCTFail(@"Ran out of time: readPushInboxMessageWithMessageId");
    }
  }];
  
  OCMVerify([self.swrve readPushInboxMessage:knownMessageId listener:OCMOCK_ANY]);
}

- (void)testReadPushInboxMessageError {
  
  UInt64 knownMessageId = 3614565938492356600;
  SwrvePushInboxResult *knownResult = [[SwrvePushInboxResult alloc] init:SwrvePushInboxResultCodeERROR :@"Server Error" :500];
  OCMExpect([self.swrve readPushInboxMessage:knownMessageId listener:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
      // Extract the listener argument from the invocation
      __unsafe_unretained id<SwrvePushInboxDelegate> delegate;
      [invocation getArgument:&delegate atIndex:3]; // The listener is the 3rd argument (0 is self, 1 is the selector, 2 is messageId, 3 is listener)
      [delegate onComplete:knownMessageId result:knownResult]; // Simulate the delegate's onComplete method being called
  });

  XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];
  NSString *knownMessageIdString = [[NSNumber numberWithUnsignedLongLong:knownMessageId] stringValue];
  [self.plugin readPushInboxMessageWithMessageId:knownMessageIdString
                                          resolver:^(id result) {
    XCTAssertNotNil(result, @"result should not be null");
    
    NSDictionary *resultDictionary = result;
    XCTAssertNotNil(resultDictionary, @"Result should not be null");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"messageId"], knownMessageIdString);
    XCTAssertEqualObjects([resultDictionary objectForKey:@"resultCode"], @"ERROR");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"errorMessage"], @"Server Error");
    XCTAssertEqual([[resultDictionary objectForKey:@"httpResponseCode"] integerValue], 500);

    [promiseResolved fulfill];
  }
                                          rejecter:^(NSString *code, NSString *message, NSError *error) {
    XCTFail(@"Rejected: this should not be rejected");
  }];
  
  // waiting for resolver to complete
  [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
    if (error) {
      XCTFail(@"Ran out of time: readPushInboxMessageWithMessageId");
    }
  }];
  
  OCMVerify([self.swrve readPushInboxMessage:knownMessageId listener:OCMOCK_ANY]);
}

- (void)testDeletePushInboxMessageSuccess {
  
  UInt64 knownMessageId = 3614565938492356600;
  SwrvePushInboxResult *knownResult = [[SwrvePushInboxResult alloc] init:SwrvePushInboxResultCodeSUCCESS :@"" :200];
  OCMExpect([self.swrve deletePushInboxMessage:knownMessageId listener:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
      // Extract the listener argument from the invocation
      __unsafe_unretained id<SwrvePushInboxDelegate> delegate;
      [invocation getArgument:&delegate atIndex:3]; // The listener is the 3rd argument (0 is self, 1 is the selector, 2 is messageId, 3 is listener)
      [delegate onComplete:knownMessageId result:knownResult]; // Simulate the delegate's onComplete method being called
  });

  XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];
  NSString *knownMessageIdString = [[NSNumber numberWithUnsignedLongLong:knownMessageId] stringValue];
  [self.plugin deletePushInboxMessageWithMessageId:knownMessageIdString
                                          resolver:^(id result) {
    XCTAssertNotNil(result, @"result should not be null");
    
    NSDictionary *resultDictionary = result;
    XCTAssertNotNil(resultDictionary, @"Result should not be null");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"messageId"], knownMessageIdString);
    XCTAssertEqualObjects([resultDictionary objectForKey:@"resultCode"], @"SUCCESS");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"errorMessage"], @"");
    XCTAssertEqual([[resultDictionary objectForKey:@"httpResponseCode"] integerValue], 200);

    [promiseResolved fulfill];
  }
                                          rejecter:^(NSString *code, NSString *message, NSError *error) {
    XCTFail(@"Rejected: this should not be rejected");
  }];
  
  // waiting for resolver to complete
  [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
    if (error) {
      XCTFail(@"Ran out of time: deletePushInboxMessageWithMessageId");
    }
  }];
  
  OCMVerify([self.swrve deletePushInboxMessage:knownMessageId listener:OCMOCK_ANY]);
}

- (void)testDeletePushInboxMessageError {
  
  UInt64 knownMessageId = 3614565938492356600;
  SwrvePushInboxResult *knownResult = [[SwrvePushInboxResult alloc] init:SwrvePushInboxResultCodeERROR :@"Server Error" :500];
  OCMExpect([self.swrve deletePushInboxMessage:knownMessageId listener:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
      // Extract the listener argument from the invocation
      __unsafe_unretained id<SwrvePushInboxDelegate> delegate;
      [invocation getArgument:&delegate atIndex:3]; // The listener is the 3rd argument (0 is self, 1 is the selector, 2 is messageId, 3 is listener)
      [delegate onComplete:knownMessageId result:knownResult]; // Simulate the delegate's onComplete method being called
  });

  XCTestExpectation *promiseResolved = [self expectationWithDescription:@"promiseResolved"];
  NSString *knownMessageIdString = [[NSNumber numberWithUnsignedLongLong:knownMessageId] stringValue];
  [self.plugin deletePushInboxMessageWithMessageId:knownMessageIdString
                                          resolver:^(id result) {
    XCTAssertNotNil(result, @"result should not be null");
    
    NSDictionary *resultDictionary = result;
    XCTAssertNotNil(resultDictionary, @"Result should not be null");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"messageId"], knownMessageIdString);
    XCTAssertEqualObjects([resultDictionary objectForKey:@"resultCode"], @"ERROR");
    XCTAssertEqualObjects([resultDictionary objectForKey:@"errorMessage"], @"Server Error");
    XCTAssertEqual([[resultDictionary objectForKey:@"httpResponseCode"] integerValue], 500);

    [promiseResolved fulfill];
  }
                                          rejecter:^(NSString *code, NSString *message, NSError *error) {
    XCTFail(@"Rejected: this should not be rejected");
  }];
  
  // waiting for resolver to complete
  [self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
    if (error) {
      XCTFail(@"Ran out of time: deletePushInboxMessageWithMessageId");
    }
  }];
  
  OCMVerify([self.swrve deletePushInboxMessage:knownMessageId listener:OCMOCK_ANY]);
}

+ (void)createDummyAsset:(NSString *)asset {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    [fileManager createDirectoryAtPath:[SwrvePluginExampleTests campaignCacheDirectory] withIntermediateDirectories:YES attributes:nil error:nil];
    NSData *dummyData = [@"TestData" dataUsingEncoding:NSASCIIStringEncoding];
    NSString *path = [[SwrvePluginExampleTests campaignCacheDirectory] stringByAppendingPathComponent:asset];
    [fileManager createFileAtPath:path contents:dummyData attributes:nil];
}

+ (NSString *)campaignCacheDirectory {
    return [[SwrvePluginExampleTests rootCacheDirectory] stringByAppendingPathComponent:@"com.ngt.msgs"];
}

+ (NSString *)rootCacheDirectory {
    static NSString *_dir = nil;
    if (!_dir) {
        _dir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    }
    return _dir;
}

+ (void)removeDummyAsset:(NSString *)asset {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *path = [[SwrvePluginExampleTests campaignCacheDirectory] stringByAppendingPathComponent:asset];
    [fileManager removeItemAtPath:path error:nil];
}

@end
