//
//  SharedFiles.m
//  appcenter-analytics
//
//  Created by Doha Tubaileh on 28/06/2022.
//

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "SharedFiles.h"
#import <React/RCTLog.h>
#import "Rainbow/Rainbow.h"
#import "RNRainbowRn.h"
#import "IEventEmmiter.h"
#import "File+DictRepresentation.h"
#import "SharedFileEventsNames.h"

#define KNumberOfFileToFetch 50

@interface SharedFiles ()
@property (strong, nonatomic, readonly) FileSharingService *fileSharingService;
@property (strong, nonatomic, readonly) ConversationsManagerService *conversationsManagerService;
@property (strong, nonatomic, readonly)  RoomsService *roomService;
@property (strong, nonatomic, readonly)  MyUser *myUser;
@property (nonatomic, readwrite)  NSInteger numberOfFilesToFetch;
@property (nonatomic, strong) IEventEmmiter *eventEmitter;
@property (nonatomic, weak) RCTBridge *bridge;
@end

@implementation SharedFiles
RCT_EXPORT_MODULE();

- (instancetype)init {
    self = [super init];
    if (self) {
        _fileSharingService = [ServicesManager sharedInstance].fileSharingService;
        _conversationsManagerService = [ServicesManager sharedInstance].conversationsManagerService;
        _roomService = [ServicesManager sharedInstance].roomsService;
        _myUser = [ServicesManager sharedInstance].myUser;
    }
    return self;
}

- (void) dealloc {

}

#pragma mark - React Bubble Files Fetch Methodes

RCT_EXPORT_METHOD(fetchFileSentInBubble:(NSString*) bubbleId) {
    NSLog(@"[SharedFiles] %@ with conversation JId: %@",NSStringFromSelector(_cmd),bubbleId);
    Room *foundedRoom = [_roomService getRoomByJid:bubbleId];
    
    // TODO: handle offset, limit, type, sort type
    if (foundedRoom) {
        [_fileSharingService fetchSharedFilesWithViewer:foundedRoom filterType:FilesFilterTypeSent owner:self.myUser.contact fromOffset:0 withLimit:10 withTypeMIME:FilterFilesAll withSortField:FileSortFieldDate completionHandler:^(NSArray<File *> * _Nullable files, NSUInteger total, NSError * _Nullable error) {
            if (error) {
                NSLog(@"[SharedFiles] %@ Failed with ERROR: %@",NSStringFromSelector(_cmd),error.description);
            }
            else {
                NSLog(@"[SharedFiles] %@ Success with file count: %lu",NSStringFromSelector(_cmd),(unsigned long)files.count);
                NSArray<File*> *sentFiles = [[NSArray alloc] init];
                NSString *myUserJID = self.myUser.contact.jid;
                NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.owner.jid == %@",myUserJID];
                sentFiles = [files filteredArrayUsingPredicate:predicate];
                [self sendEventWithName:GetSharedFileSentWithBubbleResult eventBody:[self processFilesToDictionaryArray:sentFiles]];
            }
        }];
    }
    else {
        NSLog(@"[SharedFiles] %@ ERROR: Could not find conversation with JId: %@",NSStringFromSelector(_cmd),bubbleId);
    }

}

RCT_EXPORT_METHOD(fetchFileReceivedInBubble:(NSString*) bubbleId) {
    NSLog(@"[SharedFiles] %@ with conversation JId: %@",NSStringFromSelector(_cmd),bubbleId);
    Room *foundedRoom = [_roomService getRoomByJid:bubbleId];
    // TODO: handle offset, limit, type, sort type
    if (foundedRoom) {
        [_fileSharingService fetchSharedFilesWithViewer:foundedRoom filterType:FilesFilterTypeReceived owner:self.myUser.contact fromOffset:0 withLimit:10 withTypeMIME:FilterFilesAll withSortField:FileSortFieldDate completionHandler:^(NSArray<File *> *files, NSUInteger total, NSError *error) {
            if (error) {
                NSLog(@"[SharedFiles] %@ Failed with ERROR: %@",NSStringFromSelector(_cmd),error.description);
            }
            else {
                NSLog(@"[SharedFiles] %@ Success with file count: %lu",NSStringFromSelector(_cmd),(unsigned long)files.count);
                NSArray<File*> *receivedFiles = [[NSArray alloc] init];
                NSString *myUserJID = self.myUser.contact.jid;
                NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.owner.jid != %@",myUserJID];
                receivedFiles = [files filteredArrayUsingPredicate:predicate];
                [self sendEventWithName:GetSharedFileReceivedWithBubbleResult eventBody:[self processFilesToDictionaryArray:receivedFiles]];
            }
        }];
    }
    else {
        NSLog(@"[SharedFiles] %@ ERROR: Could not find conversation with JId: %@",NSStringFromSelector(_cmd),bubbleId);
    }

}

#pragma mark - React Conversation Files Fetch Methodes


RCT_EXPORT_METHOD(fetchFileSentInConversation:(NSString*) convJId) {
    NSLog(@"[SharedFiles] %@ with conversation JId: %@",NSStringFromSelector(_cmd),convJId);
    Conversation *foundedConversation = [_conversationsManagerService getConversationWithPeerJID:convJId];
    id<PeerProtocol> conversationPeer = foundedConversation.peer;
    if (conversationPeer && [conversationPeer conformsToProtocol:@protocol(ViewerProtocol)]) {
        // Cast to id<ViewerProtocol> safely
        id<ViewerProtocol> viewerPeer = (id<ViewerProtocol>)conversationPeer;
        [_fileSharingService fetchSharedFilesWithViewer:viewerPeer filterType:FilesFilterTypeSent owner:self.myUser.contact fromOffset:0 withLimit:10 withTypeMIME:FilterFilesAll withSortField:FileSortFieldDate completionHandler:^(NSArray<File *> *files, NSUInteger total, NSError *error) {
            if (error) {
                NSLog(@"[SharedFiles] %@ Failed with ERROR: %@",NSStringFromSelector(_cmd),error.description);
            }
            else {
                NSLog(@"[SharedFiles] %@ Success with file count: %lu",NSStringFromSelector(_cmd),(unsigned long)files.count);
                NSArray<File*> *sentFiles = [[NSArray alloc] init];
                NSString *myUserJID = self.myUser.contact.jid;
                NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.owner.jid == %@",myUserJID];
                sentFiles = [files filteredArrayUsingPredicate:predicate];
                [self sendEventWithName:GetSharedFileSentWithConversationResult eventBody:[self processFilesToDictionaryArray:sentFiles]];
            }
        }];
  
    } else {
        // Handle the case where it doesn't conform to ViewerProtocol
        NSLog(@"The peer does not conform to ViewerProtocol");
    }
    
}

RCT_EXPORT_METHOD(fetchFileReceivedInConversation:(NSString*) convJId) {
    NSLog(@"[SharedFiles] %@ with conversation JId: %@",NSStringFromSelector(_cmd),convJId);
    Conversation *foundedConversation = [_conversationsManagerService getConversationWithPeerJID:convJId];
    id<PeerProtocol> conversationPeer = foundedConversation.peer;
    if (conversationPeer && [conversationPeer conformsToProtocol:@protocol(ViewerProtocol)]) {
        // Cast to id<ViewerProtocol> safely
        id<ViewerProtocol> viewerPeer = (id<ViewerProtocol>)conversationPeer;
        [_fileSharingService fetchSharedFilesWithViewer:viewerPeer filterType:FilesFilterTypeReceived owner:self.myUser.contact fromOffset:0 withLimit:10 withTypeMIME:FilterFilesAll withSortField:FileSortFieldDate completionHandler:^(NSArray<File *> * _Nullable files, NSUInteger total, NSError * _Nullable error) {
            if (error) {
                NSLog(@"[SharedFiles] %@ Failed with ERROR: %@",NSStringFromSelector(_cmd),error.description);
            }
            else {
                NSLog(@"[SharedFiles] %@ Success with file count: %lu",NSStringFromSelector(_cmd),(unsigned long)files.count);
                NSArray<File*> *receivedFiles = [[NSArray alloc] init];
                NSString *myUserJID = self.myUser.contact.jid;
                NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.owner.jid != %@",myUserJID];
                receivedFiles = [files filteredArrayUsingPredicate:predicate];
                [self sendEventWithName:GetSharedFileReceivedWithConversationResult eventBody:[self processFilesToDictionaryArray:receivedFiles]];
            }
        }];

    }else {
        NSLog(@"[SharedFiles] %@ ERROR: Could not find conversation with JId: %@",NSStringFromSelector(_cmd),convJId);

    }
}


#pragma mark - Fetch SharedFile For Peer

RCT_EXPORT_METHOD(fetchAllSharedFilesInPeer:(NSString*) peerJid isBubble:(BOOL) isBubble) {
    NSLog(@"[SharedFiles] %@ with JId: %@",NSStringFromSelector(_cmd), peerJid);
    id<PeerProtocol> foundedPeer = [self findPeerWithJid:peerJid isBubble:isBubble];
    _numberOfFilesToFetch = KNumberOfFileToFetch;

    if ([foundedPeer conformsToProtocol:@protocol(ViewerProtocol)]) {
        // Cast to id<ViewerProtocol> safely
        id<ViewerProtocol> viewerPeer = (id<ViewerProtocol>)foundedPeer;
        if (viewerPeer) {
            
            [_fileSharingService fetchSharedFilesWithViewer:viewerPeer filterType:FilesFilterTypeAll owner:(RainbowContact*)foundedPeer  fromOffset:0  withLimit:_numberOfFilesToFetch withTypeMIME:FilterFilesAll withSortField:FileSortFieldDate completionHandler:^(NSArray<File *> *files, NSUInteger total, NSError *error) {
                if (error) {
                    NSLog(@"[SharedFiles] %@ Failed with ERROR: %@",NSStringFromSelector(_cmd),error.description);
                }
                else {
                    NSLog(@"[SharedFiles] %@ Success with file count: %lu",NSStringFromSelector(_cmd),(unsigned long) files.count);
                    [self sendEventWithName:GetAllSharedFileWithPeerResult eventBody:[self processFilesToDictionaryArray:files]];
                }
            }];
        }
        else {
            NSLog(@"[SharedFiles] %@ ERROR: Could not find peer with JId: %@",NSStringFromSelector(_cmd),peerJid);
        }
    } else {
        // Handle the case where it doesn't conform to ViewerProtocol
        NSLog(@"The peer does not conform to ViewerProtocol");
    }
}

RCT_EXPORT_METHOD(loadMoreSharedFiles:(NSString*) peerJid isBubble:(BOOL) isBubble numberOfFiles:(NSInteger)numberOfFiles){
    NSLog(@"[SharedFiles] %@ with JId: %@",NSStringFromSelector(_cmd), peerJid);
    id<PeerProtocol>foundedPeer = [self findPeerWithJid:peerJid isBubble:isBubble];
    if ([foundedPeer conformsToProtocol:@protocol(ViewerProtocol)]) {
        // Cast to id<ViewerProtocol> safely
        id<ViewerProtocol> viewerPeer = (id<ViewerProtocol>)foundedPeer;
        if (viewerPeer) {
            _numberOfFilesToFetch += numberOfFiles;
            [_fileSharingService fetchSharedFilesWithViewer:viewerPeer filterType:FilesFilterTypeAll owner:self.myUser.contact fromOffset:0 withLimit:_numberOfFilesToFetch withTypeMIME:FilterFilesAll withSortField:FileSortFieldDate completionHandler:^(NSArray<File *> *files, NSUInteger total, NSError *error) {
                 if (error) {
                     NSLog(@"[SharedFiles] %@ Failed with ERROR: %@",NSStringFromSelector(_cmd),error.description);
                 }
                 else {
                     NSLog(@"[SharedFiles] %@ Success with file count: %lu",NSStringFromSelector(_cmd),(unsigned long) files.count);
                     [self sendEventWithName:GetAllSharedFileWithPeerResult eventBody:[self processFilesToDictionaryArray:files]];
                 }
             }];
        }
    }
    else {
        NSLog(@"[SharedFiles] %@ ERROR: Could not find peer with JId: %@",NSStringFromSelector(_cmd),peerJid);
    }
}

#pragma mark - React Files Actions

RCT_EXPORT_METHOD(deleteFile:(NSString*) fileId) {
    NSLog(@"[SharedFiles] %@ with fileId: %@",NSStringFromSelector(_cmd), fileId);
    File *foundedFile = [_fileSharingService getFileByRainbowID:fileId];
    if (foundedFile) {
        [_fileSharingService deleteFile:foundedFile withCompletionHandler:^(File *file, NSError *error) {
            if (error) {
                NSLog(@"[SharedFiles] %@ Failed with ERROR: %@",NSStringFromSelector(_cmd), error.description);
            }
            else {
                //TODO: send the update file list to React side!
                NSLog(@"[SharedFiles] %@ SUCCESS",NSStringFromSelector(_cmd));
            }
        }];
    }
    else {
        NSLog(@"[SharedFiles] %@ ERROR: Could not find a file with fileId: %@",NSStringFromSelector(_cmd), fileId);
    }
}

RCT_EXPORT_METHOD(downloadFile:(NSString*)fileId) {
    //get file by its rainbowId
    NSLog(@"[SharedFiles] download file");
    FileSharingService *fileSharingService = [ServicesManager sharedInstance].fileSharingService;
    File *myFile = [fileSharingService getFileByRainbowID:fileId];
    NSMutableDictionary *downloadResult = [[NSMutableDictionary alloc] init];

    if (myFile.data != nil) {
        // Its already Downloaded before!
        [downloadResult setValue:@YES forKey:@"isSuccess"];
        [downloadResult setValue:fileId forKey:@"fileId"];
        //        [downloadResult setValue:myFile.tag forKey:@"messegeId"];
        [self sendEventWithName:FileDownloadFinished eventBody:downloadResult];
        [self saveDownloadedFile:myFile];
    }
    else {
        [fileSharingService downloadDataForFile:myFile withCompletionHandler:^(File *downloadedFile, NSError *error) {
            if (error) {
                NSLog(@"[SharedFiles] download file Failed with ERROR: %@",error);
                [downloadResult setValue:@NO forKey:@"isSuccess"];
                [downloadResult setValue:error.localizedDescription forKey:@"errorMsg"];
                [downloadResult setValue:fileId forKey:@"fileId"];
                [self sendEventWithName:FileDownloadFinished eventBody: downloadResult];
            }
            else {
                NSLog(@"[SharedFiles] download file SUCCESS");
                [downloadResult setValue:@YES forKey:@"isSuccess"];
                [downloadResult setValue:fileId forKey:@"fileId"];
                [self sendEventWithName:FileDownloadFinished eventBody:downloadResult];
                [self saveDownloadedFile:downloadedFile];
            }
        }];
    }
}


#pragma mark - Internal Proccess Methodes

-(NSArray<NSDictionary*> *) processFilesToDictionaryArray: (NSArray<File*> *)filesArray {
    NSLog(@"[SharedFiles] processFilesToDictionaryArray");
    NSMutableArray<NSDictionary *> *proccesedFilesArray = [[NSMutableArray alloc] init];
    for (int i = 0; i < filesArray.count; i++) {
        File *item = filesArray[i];
        NSDictionary *fileDict = item.fileDictionaryRepresentation;
        [proccesedFilesArray addObject:fileDict];
    }
    return proccesedFilesArray;
}

-(void)saveDownloadedFile:(File*)downloadedFile {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self saveDownloadedFile:downloadedFile];
        });
        return;
    }
    //show the activity view for selecting where to download file
    NSString *documentPath = (NSString*)NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    documentPath = [documentPath stringByAppendingString:@"/"];
    NSString *filePath = [documentPath stringByAppendingString:downloadedFile.fileName];
    [downloadedFile.data writeToFile:filePath atomically:YES];
    NSFileManager *fileManager = NSFileManager.defaultManager;
    if ([fileManager fileExistsAtPath:filePath]) {
        NSURL *url = [[NSURL alloc] initFileURLWithPath:filePath];
        UIActivityViewController *controller = [[UIActivityViewController alloc]initWithActivityItems:@[url] applicationActivities:nil];

        [[[RNRainbowRn sharedManager] navigationController]  presentViewController:controller animated:YES completion:nil];
    }
}

-(id<PeerProtocol> ) findPeerWithJid:(NSString*)peerJid isBubble:(BOOL)isBubble {
    id<PeerProtocol> foundedPeer = nil;
    if (isBubble) {
        Room *room = [_roomService getRoomByJid:peerJid];
        foundedPeer = room;
    } else {
        Conversation *conversation = [_conversationsManagerService getConversationWithPeerJID:peerJid];
        foundedPeer = conversation.peer;
    }
    return foundedPeer;
}
- ( 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(@" [SharedFiles] sendEventWithName: %@", eventName);
        [eventEmitter sendEvent:eventName eventBody:eventBody];
    } else {
        NSLog(@"[SharedFiles] sendEventWithName:  Bridge is not ready. Cannot send event: %@", eventName);
    }
}
+ (BOOL)requiresMainQueueSetup
{
    return NO;
}
@end

