//
//  RangersAppLogNavStack.m
//  react-native-rangers-applog-reactnative-plugin
//
//  Created by bytedance on 2022/6/29.
//

#if __has_include(<React/RCTRootView.h>)
#import <React/RCTRootView.h>
#else
#import "RCTRootView.h"
#endif

#if __has_include(<React/UIView+React.h>)
#import <React/UIView+React.h>
#else
#import "UIView+React.h"
#endif

#if __has_include(<React/RCTLog.h>)
#import <React/RCTLog.h>
#else
#import "RCTLog.h"
#endif

#import "RangersAppLogEmitter.h"
#import "RangersAppLogModule.h"
#import "RangersAppLogModule+AutoTrack.h"
#import "RangersAppLogNavStack.h"

static NSString * const kBDReactTag       = @"react_tag";
static NSString * const kBDRNModule       = @"RangersAppLogModule";
static NSString * const kBDPageKey        = @"page_key";
static NSString * const kBDPageTitle      = @"page_title";
static NSString * const kBDPageManualKey  = @"page_manual_key";
static NSString * const kBDPagePath       = @"page_path";
static NSString * const kBDIsBack         = @"is_back";
static NSString * const kBDReferPageKey        = @"refer_page_key";
static NSString * const kBDReferPageTitle      = @"refer_page_title";
static NSString * const kBDReferPageManualKey  = @"refer_page_manual_key";
static NSString * const kBDReferPagePath       = @"referrer_page_path";
static NSString * const kBDReferReactTag       = @"refer_react_tag";


@interface RangersAppLogNavStack()

@property (nonatomic, strong) NSPointerArray *stack;
@property (nonatomic, strong) NSMapTable<UIViewController *, NSMutableDictionary *> *pageInfo;
@property (nonatomic, strong) NSDictionary *lastPageInfo;
@property (nonatomic, weak) UIViewController *lastVC;

@end

@implementation RangersAppLogNavStack

#pragma init

+ (instancetype)shared {
    static RangersAppLogNavStack *sharedInstance = nil;
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        sharedInstance = [self new];
    });
    
    return sharedInstance;
}

- (instancetype)init {
    self = [super init];
    self.stack = [NSPointerArray weakObjectsPointerArray];
    self.pageInfo = [NSMapTable weakToStrongObjectsMapTable];
    self.lastPageInfo = nil;
    return self;
}

- (void)dealloc {}


#pragma private

- (NSMutableDictionary *)collectPageInfo:(UIViewController *) vc {
    NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithCapacity:10];
    [info setValue:NSStringFromClass([vc class]) forKey:kBDPageKey];
    [info setValue:vc.navigationItem.title forKey:kBDPageTitle];
    return info;
}

- (NSMutableDictionary *)collectRNPageInfo:(UIViewController *) vc {
    NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithCapacity:10];
    [info setValue:vc.view.reactTag forKey:kBDReactTag];
    return info;
}

- (BOOL)checkIFullScreen:(UIViewController *) vc {
    if ([vc.view isKindOfClass:[RCTRootView class]]) {
        return YES;
    }
    
    CGSize pageSize = vc.view.frame.size;
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    if (pageSize.width >= screenSize.width && pageSize.height >= screenSize.height) {
        return YES;
    }
    
    return NO;
}

- (void)updateStack:(UIViewController *) vc{
    int count = (int) self.stack.count;
    int maxIndex = count - 1;
    for (int i = maxIndex; i >= 0; i--) {
        UIViewController *removeVC = [self.stack pointerAtIndex:i];
        if (removeVC == vc) {
            break;
        }
        
        [self.stack removePointerAtIndex:i];
        if (removeVC != nil) {
            [self.pageInfo removeObjectForKey:removeVC];
        }
    }
}

- (NSDictionary *)getReferPageInfo {
    UIViewController *lastVC = nil;
    int count = (int) self.stack.count;
    for (int maxIndex = count - 1; maxIndex >= 0; maxIndex--) {
        lastVC = [self.stack pointerAtIndex:maxIndex];
        if (lastVC != nil) {
            break;
        }
    }
    return [self genReferPage:lastVC];
}

- (NSDictionary *)genReferPage:(UIViewController *) vc {
    if (vc == nil) {
//        NSLog(@"count >>> %@", @(count));
        return nil;
    }
    
    NSDictionary *info = [self.pageInfo objectForKey:vc];
    if (info == nil) {
//        NSLog(@"info is nil >>> %@, %@", vc, info);
        return nil;
    }
    
    NSMutableDictionary *referPage = [[NSMutableDictionary alloc] initWithCapacity:5];
    NSString *pageKey = [info valueForKey:kBDPageKey];
    NSString *pageTitle = [info valueForKey:kBDPageTitle];
    NSString *pageManualKey = [info valueForKey:kBDPageManualKey];
    NSString *pagePath = [info valueForKey:kBDPagePath];
    NSNumber *reactTag = [info objectForKey:kBDReactTag];
    NSString *emptyStr = @"";
    
    [referPage setValue:(pageKey == nil ? emptyStr : pageKey) forKey:kBDReferPageKey];
    [referPage setValue:(pageTitle == nil ? emptyStr : pageTitle) forKey:kBDReferPageTitle];
    [referPage setValue:(pageManualKey == nil ? emptyStr : pageManualKey) forKey:kBDReferPageManualKey];
    [referPage setValue:(pagePath == nil ? emptyStr : pagePath) forKey:kBDReferPagePath];
    if (reactTag != nil) {
        [referPage setObject:reactTag forKey:kBDReferReactTag];
    }
//    NSLog(@"referpage >>> %@", referPage);
    return referPage;
}


#pragma public

- (void)appear:(UIViewController *) vc {
    if (![self checkIFullScreen:vc]) {
        return;
    }

//    NSLog(@">>>> appear: %@", NSStringFromClass([vc class]));
    NSDictionary *referPage = [self getReferPageInfo];
    BOOL isback = NO;
//    NSLog(@">>> has page: %@", [self.pageInfo objectForKey:vc]);
    if ([self.pageInfo objectForKey:vc] != nil) {
        isback = YES;
        if (self.lastVC != vc) {
            referPage = self.lastPageInfo;
        } else {
            referPage = nil;
        }
        [self updateStack:vc];
    }
    
    if (!isback) {
        NSMutableDictionary *info;
        if ([vc.view isKindOfClass:[RCTRootView class]]) {
            info = [self collectRNPageInfo:vc];
        } else {
            info = [self collectPageInfo:vc];
        }
        [info setValue:@(isback) forKey:kBDIsBack];
        [self.stack addPointer:(__bridge void * _Nullable)vc];
        [self.pageInfo setObject:info forKey:vc];
    }
    
    if ([vc.view isKindOfClass:[RCTRootView class]]) {
        RCTRootView *rootView = (RCTRootView *)vc.view;
        NSMutableDictionary *params = [[NSMutableDictionary alloc] initWithCapacity:2];
        [params setValue:@(isback) forKey:kBDIsBack];
        
        NSString *title = vc.navigationItem.title;
        if (title != nil) {
            [params setValue:title forKey:kBDPageTitle];
        }
        
        if (referPage != nil) {
            [params addEntriesFromDictionary:referPage];
        }
        
        [RangersAppLogEmitter sendPageShowEventByRootView:rootView withParams:params];
    }
}

- (void)disappear:(UIViewController *) vc {
    if (![self checkIFullScreen:vc]) {
        return;
    }

//    NSLog(@">>>> disappear: %@", NSStringFromClass([vc class]));
    if ([vc.view isKindOfClass:[RCTRootView class]]) {
        RCTRootView * rootView = (RCTRootView *)vc.view;
        [RangersAppLogEmitter sendPageHideEventByRootView:rootView withParams:nil];
    }
    self.lastPageInfo = [self genReferPage:vc];
    self.lastVC = vc;
}

- (void)updateRNParams:(NSDictionary *) params withReactTag:(NSNumber *) reactTag byModule:(id) module {
    for (UIViewController *vc in self.stack) {
        if (vc == nil) {
            continue;
        }
        
        if (![vc.view isKindOfClass:[RCTRootView class]]) {
            continue;
        }
        
        RCTRootView *rootView = (RCTRootView *) vc.view;
        if ([rootView.bridge moduleForName:kBDRNModule] != module) {
            continue;
        }
        
        if (![rootView.reactTag isEqual:reactTag]) {
            continue;
        }

        NSMutableDictionary *info = [self.pageInfo objectForKey:vc];
        if (info == nil) {
            continue;
        }

        [info addEntriesFromDictionary:params];
//        NSLog(@"updateRNParams >>> %@", info);
        break;
    }
}

@end
