/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions copyright [year] [name of copyright owner]".
 *
 * Copyright 2016 ForgeRock AS.
 */

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

#import "FRAApplicationAssembly.h"
#import "FRAIdentityModel.h"
#import "FRAIdentity.h"
#import "FRANotification.h"
#import "FRANotificationTableViewCell.h"
#import "FRANotificationsTableViewController.h"
#import "FRAPushMechanism.h"

static const NSInteger PENDING_SECTION = 0;
static const NSInteger COMPLETED_SECTION = 1;

@interface FRANotificationsTableViewControllerTests : XCTestCase

@end

@implementation FRANotificationsTableViewControllerTests {
    
    FRANotificationsTableViewController *viewController;
    FRAPushMechanism *pushMechanism;
    
}

- (void)setUp {
    [super setUp];
    
    pushMechanism = [FRAPushMechanism pushMechanismWithDatabase:nil identityModel:nil];
    
    // load notifications controller from storyboard
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
    viewController = [storyboard instantiateViewControllerWithIdentifier:@"NotificationsTableViewController"];
    viewController.pushMechanism = pushMechanism;
}

- (void)tearDown {
    [self simulateUnloadingOfView];
    [super tearDown];
}

- (void)testShowsNoNotificationsIfThereAreNone {
    // Given
    
    // When
    [self simulateLoadingOfView];
    
    // Then
    XCTAssertEqual([viewController numberOfSectionsInTableView:viewController.tableView], 1);
    XCTAssertEqual([viewController tableView:viewController.tableView numberOfRowsInSection:PENDING_SECTION], 0);
    XCTAssertEqual([viewController tableView:viewController.tableView numberOfRowsInSection:COMPLETED_SECTION], 0);
}

- (void)testShowsPendingNotificationsInFirstSection {
    // Given
    [pushMechanism addNotification:[self pendingNotificationReceivedAt:[NSDate date]] error:nil];
    [pushMechanism addNotification:[self pendingNotificationReceivedAt:[NSDate date]] error:nil];
    
    // When
    [self simulateLoadingOfView];
    
    // Then
    XCTAssertEqualObjects([viewController tableView:viewController.tableView titleForHeaderInSection:PENDING_SECTION], @"");
    XCTAssertEqual([viewController tableView:viewController.tableView numberOfRowsInSection:PENDING_SECTION], 2);
    XCTAssertEqual([viewController tableView:viewController.tableView numberOfRowsInSection:COMPLETED_SECTION], 0);
}

- (void)testShowsCompletedNotificationsInSecondSection {
    // Given
    [pushMechanism addNotification:[self approvedNotificationReceivedAt:[NSDate date]] error:nil];
    [pushMechanism addNotification:[self deniedNotificationReceivedAt:[NSDate date]] error:nil];
    
    // When
    [self simulateLoadingOfView];
    
    // Then
    XCTAssertEqualObjects([viewController tableView:viewController.tableView titleForHeaderInSection:COMPLETED_SECTION], @"COMPLETED");
    XCTAssertEqual([viewController tableView:viewController.tableView numberOfRowsInSection:PENDING_SECTION], 0);
    XCTAssertEqual([viewController tableView:viewController.tableView numberOfRowsInSection:COMPLETED_SECTION], 2);
}

- (void)testShowsDetailsOfPendingNotification {
    // Given
    FRANotification *pendingNotification = [self pendingNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-15.0]];
    [pushMechanism addNotification:pendingNotification error:nil];
    [self simulateLoadingOfView];
    NSIndexPath *notificationIndexPath = [NSIndexPath indexPathForRow:0 inSection:PENDING_SECTION];
    
    // When
    FRANotificationTableViewCell *cell = [self cellForRowAtIndexPath:notificationIndexPath];
    
    // Then
    XCTAssertEqualObjects(cell.status.text, @"Pending");
    XCTAssertEqualObjects(cell.time.text, [pendingNotification age]);
}

- (void)testCanSequeFromPendingNotificationToNotificationView {
    // Given
    [pushMechanism addNotification:[self pendingNotificationReceivedAt:[NSDate date]] error:nil];
    [self simulateLoadingOfView];
    NSIndexPath *notificationIndexPath = [NSIndexPath indexPathForRow:0 inSection:PENDING_SECTION];
    
    // When
    FRANotificationTableViewCell *cell = [self cellForRowAtIndexPath:notificationIndexPath];
    
    // Then
    XCTAssertEqual(cell.selectionStyle, UITableViewCellSelectionStyleDefault);
    XCTAssertEqual(cell.isUserInteractionEnabled, YES);
    XCTAssertEqual(cell.accessoryType, UITableViewCellAccessoryDisclosureIndicator);
    XCTAssertEqualObjects([viewController tableView:viewController.tableView willSelectRowAtIndexPath:notificationIndexPath], notificationIndexPath);
}

- (void)testShowsDetailsOfApprovedNotification {
    // Given
    FRANotification *approvedNotification = [self approvedNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-15.0]];
    [pushMechanism addNotification:approvedNotification error:nil];
    [self simulateLoadingOfView];
    NSIndexPath *notificationIndexPath = [NSIndexPath indexPathForRow:0 inSection:COMPLETED_SECTION];

    // When
    FRANotificationTableViewCell *cell = [self cellForRowAtIndexPath:notificationIndexPath];
    
    // Then
    XCTAssertEqualObjects(cell.status.text, @"Approved");
    XCTAssertEqualObjects(cell.time.text, [approvedNotification age]);
}

- (void)testCannotSequeFromApprovedNotificationToNotificationView {
    // Given
    [pushMechanism addNotification:[self approvedNotificationReceivedAt:[NSDate date]] error:nil];
    [self simulateLoadingOfView];
    NSIndexPath *notificationIndexPath = [NSIndexPath indexPathForRow:0 inSection:COMPLETED_SECTION];
    
    // When
    FRANotificationTableViewCell *cell = [self cellForRowAtIndexPath:notificationIndexPath];
    
    // Then
    XCTAssertEqual(cell.selectionStyle, UITableViewCellSelectionStyleNone);
    XCTAssertEqual(cell.isUserInteractionEnabled, NO);
    XCTAssertEqual(cell.accessoryType, UITableViewCellAccessoryNone);
    XCTAssertEqualObjects([viewController tableView:viewController.tableView willSelectRowAtIndexPath:notificationIndexPath], nil);
}

- (void)testShowsDetailsOfDeniedNotification {
    // Given
    FRANotification *deniedNotification = [self deniedNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-15.0]];
    [pushMechanism addNotification:deniedNotification error:nil];
    [self simulateLoadingOfView];
    NSIndexPath *notificationIndexPath = [NSIndexPath indexPathForRow:0 inSection:COMPLETED_SECTION];
    
    // When
    FRANotificationTableViewCell *cell = [self cellForRowAtIndexPath:notificationIndexPath];
    
    // Then
    XCTAssertEqualObjects(cell.status.text, @"Denied");
    XCTAssertEqualObjects(cell.time.text, [deniedNotification age]);
}

- (void)testCannotSequeFromDeniedNotificationToNotificationView {
    // Given
    [pushMechanism addNotification:[self deniedNotificationReceivedAt:[NSDate date]] error:nil];
    [self simulateLoadingOfView];
    NSIndexPath *notificationIndexPath = [NSIndexPath indexPathForRow:0 inSection:COMPLETED_SECTION];
    
    // When
    FRANotificationTableViewCell *cell = [self cellForRowAtIndexPath:notificationIndexPath];
    
    // Then
    XCTAssertEqual(cell.selectionStyle, UITableViewCellSelectionStyleNone);
    XCTAssertEqual(cell.isUserInteractionEnabled, NO);
    XCTAssertEqual(cell.accessoryType, UITableViewCellAccessoryNone);
    XCTAssertEqualObjects([viewController tableView:viewController.tableView willSelectRowAtIndexPath:notificationIndexPath], nil);
}

// FIXME: testSortsPendingNotificationsChronologically fails due to unmet assertions in UIKit
//- (void)testSortsPendingNotificationsChronologically {
//    // Given
//    FRANotification *firstNotification = [self pendingNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-15.0]];
//    FRANotification *secondNotification = [self pendingNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-240.0]];
//    FRANotification *thirdNotification = [self pendingNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-300.0]];
//    // NB. Add notifications out of order to confirm that they get sorted after retrieval from the push mechanism
//    [pushMechanism addNotification:secondNotification error:nil];
//    [pushMechanism addNotification:thirdNotification error:nil];
//    [pushMechanism addNotification:firstNotification error:nil];
//    
//    // When
//    FRANotificationTableViewCell *firstCell = [self cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:PENDING_SECTION]];
//    FRANotificationTableViewCell *secondCell = [self cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:PENDING_SECTION]];
//    FRANotificationTableViewCell *thirdCell = [self cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:PENDING_SECTION]];
//    
//    // Then
//    XCTAssertEqualObjects(firstCell.time.text, [firstNotification age]);
//    XCTAssertEqualObjects(secondCell.time.text, [secondNotification age]);
//    XCTAssertEqualObjects(thirdCell.time.text, [thirdNotification age]);
//}

- (void)testSortsCompletedNotificationsChronologically {
    // Given
    FRANotification *firstNotification = [self approvedNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-15.0]];
    FRANotification *secondNotification = [self deniedNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-240.0]];
    FRANotification *thirdNotification = [self approvedNotificationReceivedAt:[NSDate dateWithTimeIntervalSinceNow:-300.0]];
    // NB. Add notifications out of order to confirm that they get sorted after retrieval from the push mechanism
    [pushMechanism addNotification:secondNotification error:nil];
    [pushMechanism addNotification:thirdNotification error:nil];
    [pushMechanism addNotification:firstNotification error:nil];
    
    // When
    FRANotificationTableViewCell *firstCell = [self cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:COMPLETED_SECTION]];
    FRANotificationTableViewCell *secondCell = [self cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:COMPLETED_SECTION]];
    FRANotificationTableViewCell *thirdCell = [self cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:COMPLETED_SECTION]];
    
    // Then
    XCTAssertEqualObjects(firstCell.time.text, [firstNotification age]);
    XCTAssertEqualObjects(secondCell.time.text, [secondNotification age]);
    XCTAssertEqualObjects(thirdCell.time.text, [thirdNotification age]);
}

- (void)simulateLoadingOfView {
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_8_4) {
        [viewController view]; // force IBOutlets etc to be initialized
    } else {
        [viewController loadViewIfNeeded]; // force IBOutlets etc to be initialized
    }
    XCTAssertNotNil(viewController.view);
    [viewController viewWillAppear:YES];
}

- (void)simulateUnloadingOfView {
    [viewController viewWillDisappear:YES];
}

- (FRANotification *)pendingNotificationReceivedAt:(NSDate *)timeReceived {
    return [FRANotification notificationWithDatabase:nil
                                       identityModel:nil
                                           messageId:@"dummy"
                                           challenge:@"dummy"
                                        timeReceived:timeReceived
                                          timeToLive:120.0
                              loadBalancerCookieData:@"01"];
}

- (FRANotification *)approvedNotificationReceivedAt:(NSDate *)timeReceived {
    FRANotification *notification = [self pendingNotificationReceivedAt:timeReceived];
    [notification approveWithHandler:nil error:nil];
    return notification;
}

- (FRANotification *)deniedNotificationReceivedAt:(NSDate *)timeReceived {
    FRANotification *notification = [self pendingNotificationReceivedAt:timeReceived];
    [notification denyWithHandler:nil error:nil];
    return notification;
}

- (FRANotificationTableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    return (FRANotificationTableViewCell *)
            [viewController tableView:viewController.tableView cellForRowAtIndexPath:indexPath];
}

@end
