#import "TGUserInfoController.h"

#import "TGStringUtils.h"

#import "TGBridgeBotSignals.h"
#import "TGBridgeUserInfoSignals.h"
#import "TGBridgePeerSettingsSignals.h"
#import "TGBridgeUser.h"
#import "TGBridgeBotInfo.h"
#import "TGBridgeUserCache.h"
#import "TGUserHandle.h"

#import "TGBridgeChat.h"

#import "TGTableDeltaUpdater.h"

#import "WKInterfaceTable+TGDataDrivenTable.h"
#import "TGInterfaceMenu.h"

#import "TGUserInfoHeaderController.h"
#import "TGUserHandleRowController.h"

#import "TGProfilePhotoController.h"
#import "TGNeoConversationController.h"

NSString *const TGUserInfoControllerIdentifier = @"TGUserInfoController";

@implementation TGUserInfoControllerContext

- (instancetype)initWithUser:(TGBridgeUser *)user
{
    self = [super init];
    if (self != nil)
    {
        _user = user;
    }
    return self;
}

- (instancetype)initWithUserId:(int32_t)userId
{
    self = [super init];
    if (self != nil)
    {
        _userId = userId;
    }
    return self;
}

- (instancetype)initWithChannel:(TGBridgeChat *)channel
{
    self = [super init];
    if (self != nil)
    {
        _channel = channel;
    }
    return self;
}

@end

@interface TGUserInfoController () <TGTableDataSource>
{
    SMetaDisposable *_userDisposable;
    SMetaDisposable *_botInfoDisposable;
    SMetaDisposable *_peerSettingsDisposable;
    SMetaDisposable *_updateSettingsDisposable;
    
    TGUserInfoControllerContext *_context;
    TGBridgeUser *_userModel;
    TGBridgeBotInfo *_botInfo;
    bool _muted;
    bool _blocked;
    
    TGBridgeChat *_channelModel;
    
    NSArray *_handleModels;
    NSArray *_currentHandleModels;
    
    TGInterfaceMenu *_menu;
}
@end

@implementation TGUserInfoController

- (instancetype)init
{
    self = [super init];
    if (self != nil)
    {
        _userDisposable = [[SMetaDisposable alloc] init];
        _peerSettingsDisposable = [[SMetaDisposable alloc] init];
        _updateSettingsDisposable = [[SMetaDisposable alloc] init];
    
        [self.table _setInitialHidden:true];
        self.table.tableDataSource = self;
    }
    return self;
}

- (void)dealloc
{
    [_userDisposable dispose];
    [_botInfoDisposable dispose];
    [_peerSettingsDisposable dispose];
    [_updateSettingsDisposable dispose];
}

- (void)configureWithContext:(TGUserInfoControllerContext *)context
{
    _context = context;
    
    if (context.channel != nil)
        [self configureWithChannelContext:context];
    else
        [self configureWithUserContext:context];
}

- (void)configureWithChannelContext:(TGUserInfoControllerContext *)context
{
    _channelModel = context.channel;
    
    self.title = _channelModel.isChannelGroup ? TGLocalized(@"Watch.GroupInfo.Title") : TGLocalized(@"Watch.ChannelInfo.Title");
    
    [self updateChannelHandles];
    
    __weak TGUserInfoController *weakSelf = self;
    [self performInterfaceUpdate:^(bool animated)
    {
        __strong TGUserInfoController *strongSelf = weakSelf;
        if (strongSelf == nil)
            return;
        
        [strongSelf reloadData];
    }];
    
    [self setupPeerSettingsWithId:_channelModel.identifier];
}

- (void)configureWithUserContext:(TGUserInfoControllerContext *)context
{
    self.title = TGLocalized(@"Watch.UserInfo.Title");
    
    int32_t userId = (_context.user != nil) ? _context.user.identifier : _context.userId;
    SSignal *remoteUserSignal = [TGBridgeUserInfoSignals userInfoWithUserId:userId];
    
    SSignal *userSignal = nil;
    
    TGBridgeUser *cachedUser = [[TGBridgeUserCache instance] userWithId:userId];
    if (cachedUser == nil)
        cachedUser = _context.user;
    
    if (cachedUser != nil)
        userSignal = [[SSignal single:cachedUser] then:remoteUserSignal];
    else
        userSignal = remoteUserSignal;
    
    __weak TGUserInfoController *weakSelf = self;
    [_userDisposable setDisposable:[[userSignal deliverOn:[SQueue mainQueue]] startWithNext:^(TGBridgeUser *user)
    {
        __strong TGUserInfoController *strongSelf = weakSelf;
        if (strongSelf == nil)
            return;
        
        strongSelf->_userModel = user;
        [[TGBridgeUserCache instance] storeUser:user];
        
        if ([strongSelf _userIsBot] && (strongSelf->_botInfo == nil || user.botVersion > strongSelf->_botInfo.version))
        {
            strongSelf->_botInfoDisposable = [[SMetaDisposable alloc] init];
            [strongSelf->_botInfoDisposable setDisposable:[[TGBridgeBotSignals botInfoForUserId:user.identifier] startWithNext:^(TGBridgeBotInfo *botInfo)
            {
                __strong TGUserInfoController *strongSelf = weakSelf;
                if (strongSelf == nil)
                    return;

                strongSelf->_botInfo = botInfo;
                
                [strongSelf updateUserHandles];
                
                [strongSelf performInterfaceUpdate:^(bool animated)
                {
                    __strong TGUserInfoController *strongSelf = weakSelf;
                    if (strongSelf == nil)
                        return;
                    
                    [strongSelf reloadData];
                }];
            }]];
        }
      
        [strongSelf updateUserHandles];
        
        [strongSelf performInterfaceUpdate:^(bool animated)
        {
            __strong TGUserInfoController *strongSelf = weakSelf;
            if (strongSelf == nil)
                return;
            
            [strongSelf reloadData];
        }];
    }]];
    
    [self setupPeerSettingsWithId:userId];
}

- (void)setupPeerSettingsWithId:(int64_t)peerId
{
    __weak TGUserInfoController *weakSelf = self;
    [_peerSettingsDisposable setDisposable:[[TGBridgePeerSettingsSignals peerSettingsWithPeerId:peerId] startWithNext:^(NSDictionary *next)
    {
        __strong TGUserInfoController *strongSelf = weakSelf;
        if (strongSelf == nil)
            return;
        
        TGBridgePeerNotificationSettings *settings = next[@"notifications"];
        bool blocked = [next[@"blocked"] boolValue];
        bool muted = (settings.muteFor > 0);
        
        strongSelf->_blocked = blocked;
        strongSelf->_muted = muted;
        
        [strongSelf performInterfaceUpdate:^(bool animated)
        {
            __strong TGUserInfoController *strongSelf = weakSelf;
            if (strongSelf == nil)
                return;
            
            [strongSelf reloadData];
        }];
    }]];
}

- (void)updateUserHandles
{
    NSMutableArray *handles = [[NSMutableArray alloc] init];
    if ([self _userHasPhone])
    {
        [handles addObject:[[TGUserHandle alloc] initWithHandle:_userModel.prettyPhoneNumber type:TGLocalized(@"UserInfo.GenericPhoneLabel") handleType:TGUserHandleTypePhone data:_userModel.phoneNumber]];
    }
    if ([self _userHasUsername])
    {
        [handles addObject:[[TGUserHandle alloc] initWithHandle:[NSString stringWithFormat:@"@%@", self->_userModel.userName] type:TGLocalized(@"Profile.Username") handleType:TGUserHandleTypeUndefined data:nil]];
    }
    if ([self _botHasDescription])
    {
        [handles addObject:[[TGUserHandle alloc] initWithHandle:_botInfo.shortDescription type:TGLocalized(@"Profile.BotInfo") handleType:TGUserHandleTypeDescription data:nil]];
    }
    
    _handleModels = handles;
}

- (void)updateChannelHandles
{
    NSMutableArray *handles = [[NSMutableArray alloc] init];
    if (_channelModel.userName.length > 0)
    {
        [handles addObject:[[TGUserHandle alloc] initWithHandle:[NSString stringWithFormat:@"telegram.me/%@", self->_channelModel.userName] type:TGLocalized(@"Channel.LinkItem") handleType:TGUserHandleTypeUndefined data:nil]];
    }
    if (_channelModel.about.length > 0)
    {
        [handles addObject:[[TGUserHandle alloc] initWithHandle:_channelModel.about type:TGLocalized(@"Channel.AboutItem") handleType:TGUserHandleTypeDescription data:nil]];
    }
    
    _handleModels = handles;
}

- (void)reloadData
{
    NSArray *currentHandles = _currentHandleModels;
    _currentHandleModels = _handleModels;
    
    self.activityIndicator.hidden = true;
    
    NSInteger numberOfRows = [self numberOfRowsInTable:self.table section:0];
    if (self.table.numberOfRows == numberOfRows + 1)
    {
        [self.table reloadHeader];
        
        NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
        for (NSInteger i = 0; i < numberOfRows; i++)
            [indexPaths addObject:[TGIndexPath indexPathForRow:i inSection:0]];
        
        [self.table reloadRowsAtIndexPaths:indexPaths];
    }
    else
    {
        if (self.table.hidden)
        {
            [self.table reloadData];
        }
        else
        {
            [TGTableDeltaUpdater updateTable:self.table oldData:currentHandles newData:_currentHandleModels controllerClassForIndexPath:^Class(TGIndexPath *indexPath)
            {
                return nil;
            }];
        }
    }
    
    [self updateMenuItems];
    
    self.table.hidden = false;
}

- (void)willActivate
{
    [super willActivate];
    
    [self.table notifyVisiblityChange];
}

- (void)didDeactivate
{
    [super didDeactivate];
}

#pragma mark -

- (void)updateMenuItems
{
    [_menu clearItems];
    
    if (_menu == nil)
        _menu = [[TGInterfaceMenu alloc] initForInterfaceController:self];
    
    __weak TGUserInfoController *weakSelf = self;
    
    NSMutableArray *menuItems = [[NSMutableArray alloc] init];
    
    bool muted = _muted;
    bool blocked = _blocked;
    
    bool muteForever = _channelModel.isChannelGroup;
    int32_t muteFor = muteForever ? INT_MAX : 1;
    NSString *muteTitle = muteForever ? TGLocalized(@"Watch.UserInfo.Mute") : [NSString stringWithFormat:TGLocalized([TGStringUtils integerValueFormat:@"Watch.UserInfo.Mute_" value:muteFor]), muteFor];
    
    TGInterfaceMenuItem *muteItem = [[TGInterfaceMenuItem alloc] initWithItemIcon:muted ? WKMenuItemIconSpeaker : WKMenuItemIconMute title:muted ? TGLocalized(@"Watch.UserInfo.Unmute") : muteTitle actionBlock:^(TGInterfaceController *controller, TGInterfaceMenuItem *sender)
    {
        __strong TGUserInfoController *strongSelf = weakSelf;
        if (strongSelf == nil)
            return;
        
        TGBridgePeerNotificationSettings *settings = [[TGBridgePeerNotificationSettings alloc] init];
        settings.muteFor = muted ? 0 : (muteFor == INT_MAX ? INT_MAX : muteFor * 60 * 60);
        [strongSelf->_updateSettingsDisposable setDisposable:[[TGBridgePeerSettingsSignals updateNotificationSettingsWithPeerId:strongSelf->_userModel.identifier settings:settings] startWithNext:nil completed:^
        {
            __strong TGUserInfoController *strongSelf = weakSelf;
            if (strongSelf == nil)
                return;
            
            strongSelf->_muted = !muted;
            [strongSelf updateMenuItems];
        }]];
    }];
    [menuItems addObject:muteItem];
    
    if (_channelModel == nil)
    {
        TGInterfaceMenuItem *blockItem = [[TGInterfaceMenuItem alloc] initWithItemIcon:WKMenuItemIconBlock title:blocked ? TGLocalized(@"Watch.UserInfo.Unblock") : TGLocalized(@"Watch.UserInfo.Block") actionBlock:^(TGInterfaceController *controller, TGInterfaceMenuItem *sender)
        {
            __strong TGUserInfoController *strongSelf = weakSelf;
            if (strongSelf == nil)
                return;
            
            [strongSelf->_updateSettingsDisposable setDisposable:[[TGBridgePeerSettingsSignals updateBlockStatusWithPeerId:strongSelf->_userModel.identifier blocked:!blocked] startWithNext:nil completed:^
            {
                __strong TGUserInfoController *strongSelf = weakSelf;
                if (strongSelf == nil)
                    return;
                
                strongSelf->_blocked = !blocked;
                [strongSelf updateMenuItems];
            }]];
        }];
        [menuItems addObject:blockItem];
    }
    
    if (!_context.disallowCompose)
    {
        TGInterfaceMenuItem *composeItem = [[TGInterfaceMenuItem alloc] initWithImageNamed:@"Compose" title:TGLocalized(@"Watch.UserInfo.SendMessage") actionBlock:^(TGInterfaceController *controller, TGInterfaceMenuItem *sender)
        {
            __strong TGUserInfoController *strongSelf = weakSelf;
            if (strongSelf == nil)
                return;
            
            TGNeoConversationControllerContext *context = [[TGNeoConversationControllerContext alloc] initWithPeerId:strongSelf->_userModel.identifier];
            
            [strongSelf pushControllerWithClass:[TGNeoConversationController class] context:context];
        }];
        [menuItems addObject:composeItem];
    }

    [_menu addItems:menuItems];
}

#pragma mark - 

- (Class)headerControllerClassForTable:(WKInterfaceTable *)table
{
    return [TGUserInfoHeaderController class];
}

- (void)table:(WKInterfaceTable *)table updateHeaderController:(TGUserInfoHeaderController *)controller
{
    __weak TGUserInfoController *weakSelf = self;
    controller.isVisible = ^bool
    {
        __strong TGUserInfoController *strongSelf = weakSelf;
        if (strongSelf == nil)
            return false;
        
        return strongSelf.isVisible;
    };
    controller.avatarPressed = ^
    {
        __strong TGUserInfoController *strongSelf = weakSelf;
        if (strongSelf == nil)
            return;
        
        NSString *url = nil;
        if (strongSelf->_userModel != nil)
            url = strongSelf->_userModel.photoSmall;
        else if (strongSelf->_channelModel != nil)
            url = strongSelf->_channelModel.groupPhotoSmall;
        
        if (url != nil)
        {
            TGProfilePhotoControllerContext *context = [[TGProfilePhotoControllerContext alloc] initWithImageUrl:url];
                [strongSelf pushControllerWithClass:[TGProfilePhotoController class] context:context];
        }
    };
    
    if (_userModel != nil)
        [controller updateWithUser:_userModel context:_context.context];
    else
        [controller updateWithChannel:_channelModel];
}

- (NSUInteger)numberOfRowsInTable:(WKInterfaceTable *)table section:(NSUInteger)section
{
    return _currentHandleModels.count;
}

- (Class)table:(WKInterfaceTable *)table rowControllerClassAtIndexPath:(TGIndexPath *)indexPath
{
    TGUserHandle *userHandle = _currentHandleModels[indexPath.row];
    switch (userHandle.handleType)
    {
        case TGUserHandleTypePhone:
            return [TGUserHandleActiveRowController class];
            break;
            
        default:
            return [TGUserHandleRowController class];
    }
}

- (void)table:(WKInterfaceTable *)table updateRowController:(TGUserHandleRowController *)controller forIndexPath:(TGIndexPath *)indexPath
{
    TGUserHandle *userHandle = _currentHandleModels[indexPath.row];
    [controller updateWithUserHandle:userHandle];
}

- (void)table:(WKInterfaceTable *)table didSelectRowAtIndexPath:(TGIndexPath *)indexPath
{
    TGUserHandle *userHandle = _currentHandleModels[indexPath.row];
    switch (userHandle.handleType)
    {
        case TGUserHandleTypePhone:
            [[WKExtension sharedExtension] openSystemURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel://%@", userHandle.data]]];
            break;
            
        default:
            break;
    }
}

- (bool)_userHasPhone
{
    return (_userModel.phoneNumber.length > 0);
}

- (bool)_userHasUsername
{
    return (_userModel.userName.length > 0);
}

- (bool)_userIsBot
{
    return (_userModel.kind != TGBridgeUserKindGeneric);
}

- (bool)_botHasDescription
{
    return (_botInfo.shortDescription.length > 0);
}

#pragma mark - 

+ (NSString *)identifier
{
    return TGUserInfoControllerIdentifier;
}

@end
