//
//  Invitations.m
//  rainbow
//
//  Created by Doha Tubaileh on 9/29/19.
//  Copyright © 2019 Facebook. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "Invitations.h"
#import <React/RCTLog.h>
#import "Rainbow/Rainbow.h"
#import "IEventEmmiter.h"
#import "InvitationEventsNames.h"
#import "Invitation+DictRepresentation.h"

@interface Invitations ()
@property (strong, nonatomic) ContactsManagerService *contactManager;
@property (strong, nonatomic) NSMutableArray<Invitation *> *myInvitationArray;
@property (strong, nonatomic) NSMutableArray<NSDictionary *> *invitationReceivedArray;
@property (strong, nonatomic) NSMutableArray<NSDictionary *> *invitationSentArray;
@property (nonatomic, strong) IEventEmmiter *eventEmitter;
@property (nonatomic, weak) RCTBridge *bridge;
@end

@implementation Invitations

RCT_EXPORT_MODULE();
- (instancetype)init {
    self = [super init];
    if (self) {
        //do any init here
        [self registerToInvitationsEvents];
        _contactManager = [ServicesManager sharedInstance].contactsManagerService;
        _invitationReceivedArray = [[NSMutableArray alloc] init];
        _invitationSentArray = [[NSMutableArray alloc] init];
        _myInvitationArray = [[NSMutableArray alloc] init];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didLogin:) name:kLoginManagerDidLoginSucceeded object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didLogout:) name:kLoginManagerDidLogoutSucceeded object:nil];
    }
    return self;
}

- (void)dealloc {
    _contactManager = nil;
    _invitationSentArray = nil;
    _invitationReceivedArray = nil;
    [self unRegisterToInvitationsEvents];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kLoginManagerDidLoginSucceeded object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kLoginManagerDidLogoutSucceeded object:nil];
}

#pragma mark - Auth_Notifications

-(void) didLogin:(NSNotification *) notification {
    NSLog(@"[Invitations] connection was Succeeded");
    _invitationReceivedArray = [[NSMutableArray alloc] init];
    _invitationSentArray = [[NSMutableArray alloc] init];
    _myInvitationArray = [[NSMutableArray alloc] init];
}

-(void) didLogout:(NSNotification *) notification {
    NSLog(@"[Invitations] ConnectedUser signOut");
    _myInvitationArray = nil;
    _invitationSentArray = nil;
    _invitationReceivedArray = nil;
} 

#pragma mark - ReactNative Functions

RCT_EXPORT_METHOD(getInvitations) {
    NSLog(@"[Invitations]getInvitations");
    [self sendEventWithName:SentInvitationsUpdated eventBody:_invitationSentArray];
    [self sendEventWithName:ReceivedInvitationsUpdated eventBody:_invitationReceivedArray];
}

RCT_EXPORT_METHOD(getPendingContactInvitation) {
    NSNumber *pendingInvitationNumber = [NSNumber numberWithInteger:_contactManager.totalNbOfPendingInvitations];
    NSLog(@"[Invitations] getPendingContactInvitation %@",pendingInvitationNumber);
    [self sendEventWithName:TotalNbOfPendingInvitations eventBody:pendingInvitationNumber];
}

RCT_EXPORT_METHOD(cancelInvitation:(NSString*)invitationId) {
    NSLog(@"[Invitations]cancelInvitation");
    for (Invitation *received in _myInvitationArray) {
        if ([received.invitationID isEqualToString:invitationId]) {
            [_contactManager cancelInvitation:received completionHandler:^(NSError * _Nullable error) {
                NSLog(@"[Invitations] cancelInvitation failed");

            }];
            [self sendEventWithName:InvitationResponse eventBody:@"success"];
            return;
        }
    }
    [_eventEmitter sendEvent:InvitationResponse eventBody:@"failure"];
}

RCT_EXPORT_METHOD(declineInvitation:(NSString*)invitationId) {
    NSLog(@"[Invitations]cancelInvitation");
    for (Invitation *received in _myInvitationArray) {
        if ([received.invitationID isEqualToString:invitationId]) {
            [_contactManager declineInvitationById:invitationId completionHandler:^(NSError * _Nullable error) {
                NSLog(@"[Invitations]declineInvitation failed");
            }];
            [self sendEventWithName:InvitationResponse eventBody:@"success"];
            return;
        }
    }
    [self sendEventWithName:InvitationResponse eventBody:@"failure"];
}

RCT_EXPORT_METHOD(acceptInvitation:(NSString*)invitationId) {
    NSLog(@"[Invitations]acceptInvitation");
    for (Invitation *received in _myInvitationArray) {
        if ([received.invitationID isEqualToString:invitationId]) {
            [_contactManager acceptInvitationById:invitationId completionHandler:^(NSError * _Nullable error) {
                NSLog(@"[Invitations] acceptInvitation failed" );
            }];
            [self sendEventWithName:InvitationResponse eventBody:@"success"];
            return;
        }
    }
    [self sendEventWithName:InvitationResponse eventBody:@"failure"];
}

RCT_EXPORT_METHOD(cancelSentInvitation:(NSString*)invitationId) {
    NSLog(@"[Invitations]acceptInvitation");
    for (Invitation *received in _myInvitationArray) {
        if ([received.invitationID isEqualToString:invitationId]) {
            [_contactManager cancelInvitationById:invitationId completionHandler:^(NSError * _Nullable error) {
                NSLog(@"[Invitations] acceptInvitation failed");

            }];
            [self sendEventWithName:InvitationResponse eventBody:@"success"];
            return;
        }
    }
    [self sendEventWithName:InvitationResponse eventBody:@"failure"];
}

RCT_EXPORT_METHOD(deleteContact:(NSString*)contactJId) {
    NSLog(@"[Invitations]deleteContact");
}

RCT_EXPORT_METHOD(sendInvitation:(NSString*)contactId) {
    NSLog(@"[Invitations]sendInvitation called");
    [_contactManager searchRainbowContactWithJid:contactId withCompletionHandler:^ (Contact *foundedContact) {
        dispatch_async(dispatch_get_main_queue(), ^{
            
            if (foundedContact == nil) {
                NSLog(@"[Invitations]sendInvitation : no contact is found with id %@",contactId);
                return;
            }
            [self->_contactManager inviteContact:foundedContact completionHandler:^(Invitation * _Nullable invitation, NSError * _Nullable error) {
                NSLog(@"[Invitations]sendInvitation: inviteContact failed");

            }];
            [self sendEventWithName:InvitationResponse eventBody:nil];
        });
    }];
}

RCT_EXPORT_METHOD(sendInvitationByEmail:(NSString*)email) {
    NSLog(@"[Invitations] sendInvitationByEmail: %@", email);
    
    if (email == nil) {
        NSLog(@"[Invitations] sendInvitationByEmail: the email is null");
        return;
    }
    [_contactManager inviteByEmail:email withCompletionHandler:^(Invitation * _Nullable invitation, NSError * _Nullable error) {
        if (invitation ) {
            [self sendEventWithName:InvitationResponse eventBody:@"success"];
        } else if (error){
            [self sendEventWithName:InvitationResponse eventBody:@"Failed to send invitation"];

        }
        
    }];
    
}

#pragma mark - Notifications

-(void)didInviteContact:(NSNotification *)notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didInviteContact:notification];
        });
        return;
    }
    NSLog(@"[Invitations]didInviteContact");
}

-(void)didFailedToInviteContact:(NSNotification *)notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didFailedToInviteContact:notification];
        });
        return;
    }
    NSLog(@"[Invitations]didFailedToInviteContact");
}

-(void)didAcceptInvitation:(NSNotification *)notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didAcceptInvitation:notification];
        });
        return;
    }
    NSLog(@"[Invitations]didAcceptInvitation");
}

-(void)didAddInvitation:(NSNotification *)notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didAddInvitation:notification];
        });
        return;
    }
    NSLog(@"[Invitations]didAddInvitation");
    [self getPendingContactInvitation];
    //someone send me an invitation
    //when I send an invitation to contact
    Invitation *addedInvitation = (Invitation*)notification.object;
    [self proccessMyInvitation:addedInvitation];
    if (addedInvitation.direction == InvitationDirectionSent) {
        [self proccessInvitation:addedInvitation invitationArrayType:_invitationSentArray];
        [self sendEventWithName:SentInvitationsUpdated eventBody:_invitationSentArray];
    }
    else {
        [self proccessInvitation:addedInvitation invitationArrayType:_invitationReceivedArray];
        [self sendEventWithName:ReceivedInvitationsUpdated eventBody:_invitationReceivedArray];
    }
}

-(void)didUpdateInvitation:(NSNotification *)notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didUpdateInvitation:notification];
        });
        return;
    }
    NSLog(@"[Invitations]didUpdateInvitation");
    [self getPendingContactInvitation];
    // when I accept the invitation - firesup
    // when somebody send me an invitaion , and I decline this invitation
    // when i send an invitation , and he accept the invitation
    Invitation *updatedInvitation = (Invitation*)notification.object;
    [self proccessMyInvitation:updatedInvitation];
    if (updatedInvitation.direction == InvitationDirectionSent) {
        [self proccessInvitation:updatedInvitation invitationArrayType:_invitationSentArray];
        [self sendEventWithName:SentInvitationsUpdated eventBody:_invitationSentArray];
    }
    else {
        [self proccessInvitation:updatedInvitation invitationArrayType:_invitationReceivedArray];
        [self sendEventWithName:ReceivedInvitationsUpdated eventBody:_invitationReceivedArray];
    }
}

-(void)didRemoveInvitation:(NSNotification *)notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didRemoveInvitation:notification];
        });
        return;
    }
    NSLog(@"[Invitations]didRemoveInvitation");
    [self getPendingContactInvitation];
    //when some one remove me from his contact list ,and then send me anew invitation
    Invitation *removedInvitation = (Invitation*)notification.object;
    [self proccessMyInvitation:removedInvitation];
    if (removedInvitation.direction == InvitationDirectionSent) {
        [self proccessInvitation:removedInvitation invitationArrayType:_invitationSentArray];
        [self sendEventWithName:SentInvitationsUpdated eventBody:_invitationSentArray];
    }
    else {
        [self proccessInvitation:removedInvitation invitationArrayType:_invitationReceivedArray];
        [self sendEventWithName:ReceivedInvitationsUpdated eventBody:_invitationReceivedArray];
    }
}

#pragma mark - Others

-(void)proccessInvitation:(Invitation*)invitation invitationArrayType:(NSMutableArray<NSDictionary *>*)invitationArray  {
    NSDictionary *invitationDicFormatt = invitation.invitationDictionaryRepresentation;
    NSUInteger index = [invitationArray indexOfObjectPassingTest:
                        ^BOOL(NSDictionary *dict, NSUInteger idx, BOOL *stop)
                        {
        return [[dict objectForKey:@"invitationId"] isEqual:invitation.invitationID];
    }
                        ];
    if (index == NSNotFound && invitation.status == InvitationStatusPending) {
        [invitationArray addObject:invitationDicFormatt];
    }
    else if(index != NSNotFound) {
        [invitationArray removeObjectAtIndex:index];
    }
}

-(void)proccessMyInvitation:(Invitation*)invitation {
    NSUInteger index =  [_myInvitationArray indexOfObjectIdenticalTo:invitation];
    if (index == NSNotFound && invitation.status == InvitationStatusPending) {
        [_myInvitationArray addObject:invitation];
    }
    else if(index != NSNotFound) {
        [_myInvitationArray removeObjectAtIndex:index];
    }
}

-(void)registerToInvitationsEvents {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didInviteContact:) name:kContactsManagerServiceDidInviteContact object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFailedToInviteContact:) name:kContactsManagerServiceDidFailedToInviteContact object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAcceptInvitation:) name:kContactsManagerServiceDidAcceptInvitation object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddInvitation:) name:kContactsManagerServiceDidAddInvitation object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateInvitation:) name:kContactsManagerServiceDidUpdateInvitation object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemoveInvitation:) name:kContactsManagerServiceDidRemoveInvitation object:nil];
}

-(void)unRegisterToInvitationsEvents {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidInviteContact object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidFailedToInviteContact object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidAcceptInvitation object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidAddInvitation object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidUpdateInvitation object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidRemoveInvitation object:nil];
}
- ( IEventEmmiter *)eventEmitter {
    if (!_eventEmitter) {
        _eventEmitter = [self.bridge moduleForClass:[IEventEmmiter class]];
    }
    return _eventEmitter;
}

- (void)sendEventWithName:(NSString *)eventName eventBody:(id)eventBody {
    IEventEmmiter *eventEmitter = [self eventEmitter];
    if (eventEmitter) {
        NSLog(@" [Invitations] sendEventWithName: %@", eventName);
        [eventEmitter sendEvent:eventName eventBody:eventBody];
    } else {
        NSLog(@"[Invitations] sendEventWithName:  Bridge is not ready. Cannot send event: %@", eventName);
    }
}
+ (BOOL)requiresMainQueueSetup
{
    return NO;
}

@end

