//
//  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//

#import "SharingThreadPickerViewController.h"
#import "SignalApp.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
#import <PromiseKit/PromiseKit.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <SessionUtilitiesKit/NSString+SSK.h>
#import <SignalUtilitiesKit/OWSError.h>
#import <SessionMessagingKit/SessionMessagingKit.h>
#import <SessionUIKit/SessionUIKit.h>

NS_ASSUME_NONNULL_BEGIN

typedef void (^SendCompletionBlock)(NSError *_Nullable, TSOutgoingMessage *);
typedef void (^SendMessageBlock)(SendCompletionBlock completion);

@interface SharingThreadPickerViewController () <SelectThreadViewControllerDelegate,
    AttachmentApprovalViewControllerDelegate,
    MessageApprovalViewControllerDelegate>

@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic) TSThread *thread;
@property (nonatomic, readonly, weak) id<ShareViewDelegate> shareViewDelegate;
@property (atomic, nullable) TSOutgoingMessage *outgoingMessage;

@end

#pragma mark -

@implementation SharingThreadPickerViewController

- (instancetype)initWithShareViewDelegate:(id<ShareViewDelegate>)shareViewDelegate
{
    self = [super init];
    if (!self) {
        return self;
    }

    _shareViewDelegate = shareViewDelegate;
    self.selectThreadViewDelegate = self;

    return self;
}

#pragma mark - Dependencies

- (YapDatabaseConnection *)dbReadWriteConnection
{
    return OWSPrimaryStorage.sharedManager.dbReadWriteConnection;
}

- (YapDatabaseConnection *)dbReadConnection
{
    return OWSPrimaryStorage.sharedManager.dbReadConnection;
}

#pragma mark - UIViewController overrides

- (void)loadView
{
    [super loadView];

    self.title = NSLocalizedString(@"SHARE_EXTENSION_VIEW_TITLE", @"Title for the 'share extension' view.");
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // Loki: Customize title
    UILabel *titleLabel = [UILabel new];
    titleLabel.text = NSLocalizedString(@"Share", @"");
    titleLabel.textColor = LKColors.text;
    titleLabel.font = [UIFont boldSystemFontOfSize:25];
    self.navigationItem.titleView = titleLabel;
}

- (BOOL)canSelectBlockedContact
{
    return NO;
}

- (nullable UIView *)createHeaderWithSearchBar:(UISearchBar *)searchBar
{
    OWSAssertDebug(searchBar);

    const CGFloat contentVMargin = 0;

    UIView *header = [UIView new];
    header.backgroundColor = LKColors.navigationBarBackground;
    
    UIButton *cancelShareButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [header addSubview:cancelShareButton];

    [cancelShareButton setTitle:[CommonStrings cancelButton] forState:UIControlStateNormal];
    cancelShareButton.userInteractionEnabled = YES;

    [cancelShareButton autoPinEdgeToSuperviewMargin:ALEdgeLeading];
    [cancelShareButton autoPinEdgeToSuperviewMargin:ALEdgeBottom];
    [cancelShareButton setCompressionResistanceHigh];
    [cancelShareButton setContentHuggingHigh];

    [cancelShareButton addTarget:self
                          action:@selector(didTapCancelShareButton)
                forControlEvents:UIControlEventTouchUpInside];

    [header addSubview:searchBar];
    [searchBar autoPinEdge:ALEdgeLeading toEdge:ALEdgeTrailing ofView:cancelShareButton withOffset:6];
    [searchBar autoPinEdgeToSuperviewEdge:ALEdgeTrailing];
    [searchBar autoPinEdgeToSuperviewEdge:ALEdgeTop];
    [searchBar autoPinEdgeToSuperviewEdge:ALEdgeBottom];

    UIView *borderView = [UIView new];
    [header addSubview:borderView];

    borderView.backgroundColor = [UIColor colorWithRGBHex:0xbbbbbb];
    [borderView autoSetDimension:ALDimensionHeight toSize:0.5];
    [borderView autoPinWidthToSuperview];
    [borderView autoPinEdgeToSuperviewEdge:ALEdgeBottom];

    // UITableViewController.tableHeaderView must have its height set.
    header.frame = CGRectMake(0, 0, 0, (contentVMargin * 2 + searchBar.frame.size.height));

    return header;
}

#pragma mark - SelectThreadViewControllerDelegate

- (nullable NSString *)convertAttachmentToMessageTextIfPossible
{
    if (self.attachments.count > 1) {
        return nil;
    }
    OWSAssertDebug(self.attachments.count == 1);
    SignalAttachment *attachment = self.attachments.firstObject;
    if (!attachment.isConvertibleToTextMessage) {
        return nil;
    }
    if (attachment.dataLength >= kOversizeTextMessageSizeThreshold) {
        return nil;
    }
    NSData *data = attachment.data;
    OWSAssertDebug(data.length < kOversizeTextMessageSizeThreshold);
    NSString *_Nullable messageText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    OWSLogVerbose(@"messageTextForAttachment: %@", messageText);
    return [messageText filterStringForDisplay];
}

- (void)threadWasSelected:(TSThread *)thread
{
    OWSAssertDebug(self.attachments.count > 0);
    OWSAssertDebug(thread);

    self.thread = thread;

    if ([self tryToShareAsMessageText]) {
        return;
    }

    OWSNavigationController *approvalModal =
        [AttachmentApprovalViewController wrappedInNavControllerWithAttachments:self.attachments approvalDelegate:self];
    [self presentViewController:approvalModal animated:YES completion:nil];
}

- (BOOL)tryToShareAsMessageText
{
    OWSAssertDebug(self.attachments.count > 0);

    NSString *_Nullable messageText = [self convertAttachmentToMessageTextIfPossible];
    if (!messageText) {
        return NO;
    }

    MessageApprovalViewController *approvalVC =
        [[MessageApprovalViewController alloc] initWithMessageText:messageText
                                                            thread:self.thread
                                                          delegate:self];

    [self.navigationController pushViewController:approvalVC animated:YES];
    return YES;
}

// override
- (void)dismissPressed:(id)sender
{
    OWSLogDebug(@"tapped dismiss share button");
    [self cancelShareExperience];
}

- (void)didTapCancelShareButton
{
    OWSLogDebug(@"tapped cancel share button");
    [self cancelShareExperience];
}

- (void)cancelShareExperience
{
    [self.shareViewDelegate shareViewWasCancelled];
}

#pragma mark - AttachmentApprovalViewControllerDelegate

- (void)attachmentApproval:(AttachmentApprovalViewController *_Nonnull)attachmentApproval
     didApproveAttachments:(NSArray<SignalAttachment *> *_Nonnull)attachments
               messageText:(NSString *_Nullable)messageText
{
    [self tryToSendMessageWithBlock:^(SendCompletionBlock sendCompletion) {
        SNVisibleMessage *message = [SNVisibleMessage new];
        message.sentTimestamp = [NSDate millisecondTimestamp];
        message.text = messageText;
        TSOutgoingMessage *tsMessage = [TSOutgoingMessage from:message associatedWith:self.thread];
        [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
            [tsMessage saveWithTransaction:transaction];
        }];
        [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
            [SNMessageSender sendNonDurably:message withAttachments:attachments inThread:self.thread usingTransaction:transaction]
            .then(^(id object) {
                sendCompletion(nil, tsMessage);
            }).catch(^(NSError *error) {
                sendCompletion(error, tsMessage);
            });
        }];

        // This is necessary to show progress
        self.outgoingMessage = tsMessage;
    } fromViewController:attachmentApproval];
}

- (void)attachmentApprovalDidCancel:(AttachmentApprovalViewController *)attachmentApproval
{
    [self cancelShareExperience];
}

- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval
      didChangeMessageText:(nullable NSString *)newMessageText
{
    // no-op
}

#pragma mark - MessageApprovalViewControllerDelegate

- (void)messageApproval:(MessageApprovalViewController *)approvalViewController
      didApproveMessage:(NSString *)messageText
{
    [self tryToSendMessageWithBlock:^(SendCompletionBlock sendCompletion) {
        SNVisibleMessage *message = [SNVisibleMessage new];
        message.sentTimestamp = [NSDate millisecondTimestamp];
        message.text = messageText;
        TSOutgoingMessage *tsMessage = [TSOutgoingMessage from:message associatedWith:self.thread];
        [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
            [tsMessage saveWithTransaction:transaction];
        }];
        [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
            [SNMessageSender sendNonDurably:message withAttachments:@[] inThread:self.thread usingTransaction:transaction]
            .then(^(id object) {
                sendCompletion(nil, tsMessage);
            }).catch(^(NSError *error) {
                sendCompletion(error, tsMessage);
            });
        }];
        
        // This is necessary to show progress
        self.outgoingMessage = tsMessage;
    } fromViewController:approvalViewController];
}

- (void)messageApprovalDidCancel:(MessageApprovalViewController *)approvalViewController
{
    [self cancelShareExperience];
}

#pragma mark - Helpers

- (void)tryToSendMessageWithBlock:(SendMessageBlock)sendMessageBlock
               fromViewController:(UIViewController *)fromViewController
{

    NSString *progressTitle = NSLocalizedString(@"SHARE_EXTENSION_SENDING_IN_PROGRESS_TITLE", @"Alert title");
    UIAlertController *progressAlert = [UIAlertController alertControllerWithTitle:progressTitle
                                                                           message:nil
                                                                    preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *progressCancelAction = [UIAlertAction actionWithTitle:[CommonStrings cancelButton]
                                                                   style:UIAlertActionStyleCancel
                                                                 handler:^(UIAlertAction *_Nonnull action) {
                                                                     [self.shareViewDelegate shareViewWasCancelled];
                                                                 }];
    [progressAlert addAction:progressCancelAction];

    SendCompletionBlock sendCompletion = ^(NSError *_Nullable error, TSOutgoingMessage *message) {

        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                [fromViewController
                    dismissViewControllerAnimated:YES
                                       completion:^{
                                           OWSLogInfo(@"Sending message failed with error: %@", error);
                                           [self showSendFailureAlertWithError:error
                                                                       message:message
                                                            fromViewController:fromViewController];
                                       }];
                return;
            }

            OWSLogInfo(@"Sending message succeeded.");
            [self.shareViewDelegate shareViewWasCompleted];
        });
    };

    [fromViewController presentAlert:progressAlert
                          completion:^{
                              sendMessageBlock(sendCompletion);
                          }];
}

- (void)showSendFailureAlertWithError:(NSError *)error
                              message:(TSOutgoingMessage *)message
                   fromViewController:(UIViewController *)fromViewController
{
    OWSAssertIsOnMainThread();
    OWSAssertDebug(error);
    OWSAssertDebug(message);
    OWSAssertDebug(fromViewController);

    NSString *failureTitle = NSLocalizedString(@"SHARE_EXTENSION_SENDING_FAILURE_TITLE", @"Alert title");

    if ([error.domain isEqual:OWSSignalServiceKitErrorDomain] && error.code == OWSErrorCodeUntrustedIdentity) {
        NSString *_Nullable untrustedRecipientId = error.userInfo[OWSErrorRecipientIdentifierKey];

        NSString *failureFormat = NSLocalizedString(@"SHARE_EXTENSION_FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_FORMAT",
            @"alert body when sharing file failed because of untrusted/changed identity keys");

        SNContactContext context = [SNContact contextForThread:self.thread];
        NSString *displayName = [[LKStorage.shared getContactWithSessionID:untrustedRecipientId] displayNameFor:context] ?: untrustedRecipientId;
        NSString *failureMessage = [NSString stringWithFormat:failureFormat, displayName];

        UIAlertController *failureAlert = [UIAlertController alertControllerWithTitle:failureTitle
                                                                              message:failureMessage
                                                                       preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *failureCancelAction = [UIAlertAction actionWithTitle:[CommonStrings cancelButton]
                                                                      style:UIAlertActionStyleCancel
                                                                    handler:^(UIAlertAction *_Nonnull action) {
                                                                        [self.shareViewDelegate shareViewWasCancelled];
                                                                    }];
        [failureAlert addAction:failureCancelAction];

        if (untrustedRecipientId.length > 0) {
            UIAlertAction *confirmAction =
                [UIAlertAction actionWithTitle:[SafetyNumberStrings confirmSendButton]
                                         style:UIAlertActionStyleDefault
                                       handler:^(UIAlertAction *action) {
                                           [self confirmIdentityAndResendMessage:message
                                                                     recipientId:untrustedRecipientId
                                                              fromViewController:fromViewController];
                                       }];

            [failureAlert addAction:confirmAction];
        } else {
            // This shouldn't happen, but if it does we won't offer the user the ability to confirm.
            // They may have to return to the main app to accept the identity change.
            OWSFailDebug(@"Untrusted recipient error is missing recipient id.");
        }

        [fromViewController presentAlert:failureAlert];
    } else {
        // Non-identity failure, e.g. network offline, rate limit

        UIAlertController *failureAlert = [UIAlertController alertControllerWithTitle:failureTitle
                                                                              message:error.localizedDescription
                                                                       preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *failureCancelAction = [UIAlertAction actionWithTitle:[CommonStrings cancelButton]
                                                                      style:UIAlertActionStyleCancel
                                                                    handler:^(UIAlertAction *_Nonnull action) {
                                                                        [self.shareViewDelegate shareViewWasCancelled];
                                                                    }];
        [failureAlert addAction:failureCancelAction];

        UIAlertAction *retryAction =
            [UIAlertAction actionWithTitle:[CommonStrings retryButton]
                                     style:UIAlertActionStyleDefault
                                   handler:^(UIAlertAction *action) {
                                       [self resendMessage:message fromViewController:fromViewController];
                                   }];

        [failureAlert addAction:retryAction];
        [fromViewController presentAlert:failureAlert];
    }
}

- (void)confirmIdentityAndResendMessage:(TSOutgoingMessage *)message
                            recipientId:(NSString *)recipientId
                     fromViewController:(UIViewController *)fromViewController
{
    OWSAssertIsOnMainThread();
    OWSAssertDebug(message);
    OWSAssertDebug(recipientId.length > 0);
    OWSAssertDebug(fromViewController);

    OWSLogDebug(@"Confirming identity for recipient: %@", recipientId);

    dispatch_async(dispatch_get_main_queue(), ^(void) {
        [self resendMessage:message fromViewController:fromViewController];
    });
}

- (void)resendMessage:(TSOutgoingMessage *)tsMessage fromViewController:(UIViewController *)fromViewController
{
    OWSAssertIsOnMainThread();
    OWSAssertDebug(tsMessage);
    OWSAssertDebug(fromViewController);

    NSString *progressTitle = NSLocalizedString(@"SHARE_EXTENSION_SENDING_IN_PROGRESS_TITLE", @"Alert title");
    UIAlertController *progressAlert = [UIAlertController alertControllerWithTitle:progressTitle
                                                                           message:nil
                                                                    preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *progressCancelAction = [UIAlertAction actionWithTitle:[CommonStrings cancelButton]
                                                                   style:UIAlertActionStyleCancel
                                                                 handler:^(UIAlertAction *_Nonnull action) {
                                                                     [self.shareViewDelegate shareViewWasCancelled];
                                                                 }];
    [progressAlert addAction:progressCancelAction];

    [fromViewController
        presentAlert:progressAlert
            completion:^{
                SNVisibleMessage *message = [SNVisibleMessage from:tsMessage];
                [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
                    NSMutableArray<TSAttachmentStream *> *attachments = @[].mutableCopy;
                    for (NSString *attachmentID in tsMessage.attachmentIds) {
                        TSAttachmentStream *stream = [TSAttachmentStream fetchObjectWithUniqueID:attachmentID transaction:transaction];
                        if (![stream isKindOfClass:TSAttachmentStream.class]) { continue; }
                        [attachments addObject:stream];
                    }
                    [SNMessageSender prep:attachments forMessage:message usingTransaction: transaction];
                    [SNMessageSender sendNonDurably:message withAttachmentIDs:tsMessage.attachmentIds inThread:self.thread usingTransaction:transaction]
                    .thenOn(dispatch_get_main_queue(), ^() {
                        [self.shareViewDelegate shareViewWasCompleted];
                    })
                    .catchOn(dispatch_get_main_queue(), ^(NSError *error) {
                        [fromViewController dismissViewControllerAnimated:YES completion:^{
                            [self showSendFailureAlertWithError:error message:tsMessage fromViewController:fromViewController];
                        }];
                    });
                }];
          }];
}

@end

NS_ASSUME_NONNULL_END