//
//  Contacts.m
//  rainbow
//
//  Created by Doha Tubaileh on 8/26/19.
//  Copyright © 2019 Facebook. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "Contacts.h"
#import <React/RCTLog.h>
#import "Rainbow/Rainbow.h"
#import "Contact+DictRepresentation.h"
#import "ContactsEventsNames.h"
#import "IEventEmmiter.h"
#import <Contacts/Contacts.h>

@interface Contacts ()
@property (nonatomic, strong) NSMutableArray<NSDictionary*> *contactsDictionaryRepresentations;
@property (nonatomic, strong) NSMutableArray<NSDictionary*> *localContactsDictionaryRepresentations;
@property (nonatomic, strong) ContactsManagerService *contactsManagerService;
@property (nonatomic) BOOL populated;
@property (nonatomic) BOOL isContactViewLoaded;
@property (nonatomic, strong) IEventEmmiter *eventEmitter;
@property (nonatomic, weak) RCTBridge *bridge;

@end

@implementation Contacts
RCT_EXPORT_MODULE();

- (instancetype)init {
    self = [super init];
    if (self) {
        _contactsDictionaryRepresentations = [[NSMutableArray alloc] init];
        _localContactsDictionaryRepresentations = [[NSMutableArray alloc] init];
        _contactsManagerService = [ServicesManager sharedInstance].contactsManagerService;
        _populated = NO;
        _isContactViewLoaded = NO;
        [self registerContactEvents];
        
    }
    return self;
}

- (void)dealloc {
    _contactsDictionaryRepresentations = nil;
    [self unRegisterContactEvents];
    _populated = NO;
    _isContactViewLoaded = NO;
}

-(void)didEndPopulatingMyNetwork:(NSNotification *)notification {
    @synchronized (_contactsDictionaryRepresentations) {
        NSArray <Contact *> *contacts = [_contactsManagerService.myNetworkContacts sortedArrayUsingComparator:^NSComparisonResult(Contact *obj1, Contact *obj2) {
            return [obj1.fullName compare:obj2.fullName];
        }];
        
        for (Contact *contact in contacts) {
            [self updateContactsArray:contact];
        }
        
        if (_contactsDictionaryRepresentations.count > 0) {
            NSLog(@"[Contacts] didEndPopulatingMyNetwork: with contactList count: %lu",(unsigned long)_contactsDictionaryRepresentations.count);
            [self sendEventWithName:ContactsUpdated eventBody:self->_contactsDictionaryRepresentations];
            _populated = YES;
        }
    }
}

#pragma mark - ReactNative_functions

RCT_EXPORT_METHOD(getContacts) {
    NSLog(@"[Contacts] Get Roster Contacts");
    _contactsDictionaryRepresentations = [[NSMutableArray alloc] init];
    _isContactViewLoaded = YES;
    _populated = NO;
    if (!_populated) {
        [self didEndPopulatingMyNetwork:nil];
    }
}

RCT_EXPORT_METHOD(fetchContactDetails:(NSString*)jid) {
    NSLog(@"[Contacts] fetchContactDetail with jid: %@",jid);
    [_contactsManagerService searchRainbowContactWithJid:jid withCompletionHandler:^(Contact *foundContact) {
        //there is an issue when fetching the contact for the first time, it will trigger didUpdate contact and save it in the cache.vcardPopulated
        
        if (foundContact != nil) {
            //this contact is stred in the chach, no need to ferch its detail again
            NSDictionary *newContactDetail = [foundContact contactDictionaryRepresentation];
            [self sendEventWithName:ContactsDetailUpdated eventBody:newContactDetail];
            
            [self->_contactsManagerService fetchRemoteContactDetail:foundContact];
        }
    }];
}

RCT_EXPORT_METHOD(addContactToRoster:(NSString*)jid) {
    NSLog(@"[Contacts] addContactToRoster with jid: %@",jid);
    [_contactsManagerService searchRainbowContactWithJid:jid withCompletionHandler:^(Contact *foundContact) {
        if (foundContact != nil) {
            [self->_contactsManagerService inviteContact:foundContact completionHandler:^(Invitation * _Nullable invitation, NSError * _Nullable error) {
                NSLog(@"[Contacts] addContactToRoster has failed");
            }];
        }
    }];
}

RCT_EXPORT_METHOD(removeContact:(NSString*)jid) {
    NSLog(@"[Contacts] addContactToRoster with jid: %@",jid);
    [_contactsManagerService searchRainbowContactWithJid:jid withCompletionHandler:^(Contact *foundContact) {
        if (foundContact != nil) {
            [self->_contactsManagerService removeContactFromMyNetwork:foundContact];
            [self sendEventWithName:RemoveContact eventBody:@"success"];
        }
    }];
}





#pragma mark - Notifications

-(void)didAddContact:(NSNotification *)notification {
    
    if (!_populated) {
        return;
    }
    Contact *contact = notification.object;
    NSLog(@"[Contacts] Did add contact with Contact: %@",contact.fullName);
    [self updateContactsArray:contact];
    
}

-(void)didRemoveContact:(NSNotification *) notification {
    
    if (!_populated) {
        return;
    }
    
    NSLog(@"[Contacts] Did remove contact");
    Contact *contact = notification.object;
    [self updateContactsArray:contact];
    
}

-(void)didUpdateContact:(NSNotification *)notification {
    
    if (!_populated) {
        return;
    }
    
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didUpdateContact:notification];
        });
        return;
    }
    
    NSLog(@"[Contacts] Did update with contact");
    NSDictionary *userInfo = (NSDictionary *)notification.object;
    
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    [self performSelector:@selector(sendContactsUpdateEvent:) withObject:userInfo afterDelay:1.0];
    
}

-(void) sendContactsUpdateEvent:(NSDictionary*)userInfo {
    
    RainbowContact *contact = [userInfo objectForKey:kContactKey];
    NSArray<NSString *> *changedKeys = [userInfo objectForKey:kChangedAttributesKey];
    
    NSLog(@"sendContactsUpdateEvent with attribute: %@ and contact: %@",changedKeys,contact.fullName);
    // Send contact Precence
    NSDictionary *newPresence = [contact getPresenceDictRepresentation];
    [self sendEventWithName:PresenceUpdated eventBody:newPresence];
    
    [self updateContactsArray:contact];
    
    if (([changedKeys containsObject:@"isInRoster"] || [changedKeys containsObject:@"lastActivityDate"] || [changedKeys containsObject:@"requestedInvitation"]) && self->_isContactViewLoaded) {
        
        if (_contactsDictionaryRepresentations.count > 0) {
            [self sendEventWithName:ContactsUpdated eventBody:_contactsDictionaryRepresentations];
        }
    }
    else if ([changedKeys containsObject:@"phoneNumbers"]) {
        //fetch the contact detail including phones number
        NSDictionary *newContactDetail = [contact contactDictionaryRepresentation];
        [self sendEventWithName:ContactsDetailUpdated eventBody:newContactDetail];
    }
    
}

#pragma mark - Intitation events handler

-(void) didUpdateInvitation:(NSNotification *) notification {
    NSLog(@"[Contacts] Did update invitation");
    
    Invitation *invitation = (Invitation *)notification.object;
    
    if (invitation.status == InvitationStatusAccepted ||
        invitation.status == InvitationStatusAutoAccepted) {
        //add this contact to my contact list
        Contact *invitedContact = (Contact *)[invitation contact];
        [self updateContactsArray:invitedContact];
    }
    
}

-(void) didInviteContact:(NSNotification *) notification {
    NSLog(@"[Contacts] didInviteContact");
    [self sendEventWithName:AddContactToRoster eventBody:@"success"];
}

-(void) didFailedToInviteContact:(NSNotification *) notification {
    NSError *error = (NSError *)notification.object;
    NSLog(@"[Contacts] didFailedToInviteContact with Error: %@",error);
    [self sendEventWithName:AddContactToRoster eventBody:@"Error"];
    
}
#pragma mark - Others

-(void) updateContactsArray:(RainbowContact *) contact {
    @synchronized (_contactsDictionaryRepresentations) {
        // Ignore myself
        if (contact == [ServicesManager sharedInstance].myUser.contact) {
            return;
        }
        if (contact.fullName == nil) {
            return;
        }
        // Ignore bots like Emily
        if(contact.isBot) {
            return;
        }
        // Ignore temporary invited user
        if(contact.sentInvitationId != nil) {
            return;
        }
        
        NSMutableArray<NSDictionary*> *contactsDictionaryRepresentations = _contactsDictionaryRepresentations.mutableCopy;
        NSDictionary *contactDic = contact.contactDictionaryRepresentation;
        NSString *contactJid = contact.jid;
        
        NSArray *contactsJids = [contactsDictionaryRepresentations valueForKey:@"jId"];
        NSInteger index = [contactsJids indexOfObject:contactJid];
        
        if(!contact.isInRoster && !(contact.sentInvitationId != nil)) {
            // Check if contact has been removed from my network
            if(index != NSNotFound) {
                [contactsDictionaryRepresentations removeObjectAtIndex:index];
            }
        }
        else {
            if (index != NSNotFound) {
                [contactsDictionaryRepresentations replaceObjectAtIndex:index withObject:contactDic];
            }
            else {
                [contactsDictionaryRepresentations addObject:contactDic];
            }
        }
        _contactsDictionaryRepresentations = contactsDictionaryRepresentations;
    }
    
}

-(void)registerContactEvents {
    //Listen to for each contact changes
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddContact:) name:kContactsManagerServiceDidAddContact object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemoveContact:) name:kContactsManagerServiceDidRemoveContact object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateContact:) name:kContactsManagerServiceDidUpdateContact object:nil];
    //Listen to Contact Invitation changes
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateInvitation:) name:kContactsManagerServiceDidUpdateInvitation object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didInviteContact:) name:kContactsManagerServiceDidInviteContact object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFailedToInviteContact:) name:kContactsManagerServiceDidFailedToInviteContact object:nil];
}

-(void)unRegisterContactEvents {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidAddContact object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidRemoveContact object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidUpdateContact object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidUpdateInvitation object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidEndPopulatingMyNetwork object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidInviteContact object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kContactsManagerServiceDidFailedToInviteContact object:nil];
}

+ (BOOL)requiresMainQueueSetup
{
    return NO;
}

-(NSArray*)contactRearranging:(NSArray<Contact*>*)contacts{
    NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:@"fullName" ascending:YES];
    contacts = [contacts sortedArrayUsingDescriptors:@[sd]];
    return contacts;
}
- ( 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(@" [Contacts] sendEventWithName: %@ ", eventName);
        [eventEmitter sendEvent:eventName eventBody:eventBody];
    } else {
        NSLog(@"[Contacts] sendEventWithName:  Bridge is not ready. Cannot send event: %@", eventName);
    }
}

@end
