//
//  Phobos.m
//  GengmeiDoctor
//
//  Created by Thierry on 16/1/26.
//  Copyright © 2016年 wanmeizhensuo. All rights reserved.
//

#import "NewPhobos.h"
#import <AdSupport/AdSupport.h>
#import "UIResponder+PhobosPV.h"
#import "PhobosUtil.h"
#import "PhobosCustomVisibleController.h"
#import <GMCache/GMCache.h>
#import <mach/mach_time.h>
#import "PhobosDataManager.h"
#import "PhobosSendManager.h"

static NewPhobos *_sharedClient;

@interface NewPhobos ()
@property (strong, nonatomic) UIViewController *visibleController;
@property (strong, nonatomic) NSDateFormatter *dateFormatter;
@property (strong, nonatomic) NSString *appName;
@property (strong, nonatomic) NSString *channelId;
@property (strong, nonatomic) NSString *appVersion;
@property (strong, nonatomic) NSString *sessionId;
/* 每一条埋点数据的物理ID，自增，生命周期和sessionId相同。特别注意：在sessionOver的时候，要把他置为0 */
@property (assign, nonatomic) NSInteger serialId;

@end

@implementation NewPhobos

static dispatch_semaphore_t _phobos_semaphore;

+ (NewPhobos *)clientWithAppName:(NSString *)appName channelId:(NSString *)channelId{
    NewPhobos.sharedClient.appName = appName;
    NewPhobos.sharedClient.channelId = channelId;
    return NewPhobos.sharedClient;
}

- (instancetype)init {
    if (self = [super init]) {
        _phobos_semaphore = dispatch_semaphore_create(1);
        _appName = @"";
        _channelId = @"";
        _logEnabled = NO;
        _userId = @"";
        _netStatus = @"";
        _currentCityId = @"";
        _serverAPI = @"";
        _greyType = @"";
        _userType = [[NSMutableDictionary alloc] init];
        _appVersion = [PhobosUtil getAppVersion];
        _signingType = PhobosSigningTypeUndefined;
        
        [self setupNotification];
        [self handleSessionStart];
    }
    return self;
}

+ (id)allocWithZone:(struct _NSZone *)zone {
    return NewPhobos.sharedClient;
}

- (id)copyWithZone:(struct _NSZone *)zone {
    return NewPhobos.sharedClient;
}

+ (instancetype)sharedClient {// 使用单例设计模式，保证Phobos只有一个实例，并提供了全局访问点
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedClient = [[super allocWithZone:NULL] init];
    });
    return _sharedClient;
}

- (void)dealloc{
    if (self) {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
}

- (void)setUserType:(NSMutableDictionary *)userType {
    if (userType == nil && userType.allKeys.count == 0) {
        return;
    }
    NSArray *newKeys = userType.allKeys;
    NSArray *oldKeys = _userType.allKeys;
    for (NSString *newKey in newKeys) {
        if ([oldKeys containsObject:newKey]) {
            NSString *newValue = [[userType objectForKey:newKey] stringValue];
            NSString *oldValue = [[_userType objectForKey:newKey] stringValue];
            if (![newValue isEqualToString:oldValue]) {
                [_userType setObject:[userType objectForKey:newKey] forKey:newKey];
            }
        } else {
            [_userType setObject:[userType objectForKey:newKey] forKey:newKey];
        }
    }
}

/**
   在APP启动、从后台到前台的时候需要记录device_opened埋点
 */
- (void)handleEventDeviceOpened{

    /** 每次打开APP埋点 **/
    NSDictionary *dict = @{@"build_cpu_abi": [PhobosUtil currentDeviceCPUType],
                           @"cpu_count": [PhobosUtil currentDeviceCPUCount],
                           @"mac_address": [PhobosUtil getMacAddress],
                           @"phone_operator": [PhobosUtil getTelephonyInfo],
                           @"total_memory": [PhobosUtil getTotalMemorySize],
                           @"run_time": [PhobosUtil deviceRunTime],
                           @"uuid": [PhobosUtil deviceId],
                           @"build_version_release": [[UIDevice currentDevice] systemVersion],
                           };
    [NewPhobos track:@"device_opened" attributes:dict sendNow:YES];
}

- (UIViewController *)visibleController {
    if (self.getTopController) {
        id target = self.getTopController();

        if ([target conformsToProtocol:NSProtocolFromString(@"PhobosCustomVisibleController")]) {
            target = [target performSelector:@selector(phobosVisibleController)];
        }
        return target;
    }
    return nil;
}

#pragma mark - notification handler
/**
 *  @brief 设置对APP的通知监听
 *
 *  @since 0.0.1
 */
- (void)setupNotification{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppFinishLaunch) name:UIApplicationDidFinishLaunchingNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppInForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppInBackgound) name:UIApplicationDidEnterBackgroundNotification object:nil];
}

/**
 *  @brief 每次打开APP或返回前台，即Session开始的时候的处理
 *  @notification didFinishLaunch和willEnterForeground的时候都需要记录
 *
 *  @since 0.0.1
 */
- (void)handleSessionStart{
    _serialId = 0;
    _sessionId = [[NSUUID UUID] UUIDString];

    [GMCache storeObjectAtDocumentPathWithkey:PhobosBeginTime object:[PhobosUtil currentTime]];
}

/**
 *  @brief 应用打开时的处理
 *
 *  @since 0.0.1
 */
- (void)handleAppFinishLaunch {
    phobosLog(@"handleAppFinishLaunch");
    [self handleSessionStart];
    [self handleEventDeviceOpened];
}

/**
 *  @brief 应用进入前台的处理
 *
 *  @since 0.0.1
 */
- (void)handleAppInForeground {
    phobosLog(@"handleAppInForeground");
    [self handleSessionStart];
    [self handleEventDeviceOpened];
    [NewPhobos disposeSendDataWithImmediately:NO];
    [self handlePVEventAppInForeground];
}

/**
 *  @brief 应用进入后台的处理
 *
 *  @since 0.0.1
 */
- (void)handleAppInBackgound {
    phobosLog(@"handleAppInBackgound");
    [self handlePVEventAppInBackgound];
    [self handleSessionOver];
    [NewPhobos disposeSendDataWithImmediately:NO];
}

/**
 *  @brief 会话结束时候的处理
 *
 *  @since 0.0.1
 */
- (void)handleSessionOver{

    //进入后台的同时，把记录时间同步到服务端，把已使用时间清空
    double beginTime = [[GMCache fetchObjectAtDocumentPathWithkey:PhobosBeginTime] doubleValue];
    if (beginTime == 0) {
        return;
    }
    NSDate *date = [NSDate date];
    double endTime = [date timeIntervalSince1970];
    NSString *usedTime = [NSString stringWithFormat:@"%ld",(long)(endTime - beginTime)];
    NSDictionary *dict = @{@"duration":usedTime,
                           @"build_cpu_abi": [PhobosUtil currentDeviceCPUType],
                           @"cpu_count": [PhobosUtil currentDeviceCPUCount],
                           @"mac_address": [PhobosUtil getMacAddress],
                           @"phone_operator": [PhobosUtil getTelephonyInfo],
                           @"total_memory": [PhobosUtil getTotalMemorySize],
                           @"run_time": [PhobosUtil deviceRunTime],
                           @"uuid": [PhobosUtil deviceId],
                           @"build_version_release": [[UIDevice currentDevice] systemVersion],
                           };
    [NewPhobos track:@"on_app_session_over" attributes:dict];
    [GMCache removeObjectAtDocumentPathWithkey:PhobosBeginTime];
    //当前session结束之后，把id置为0
    _serialId = 0;
}

/**
 APP从后台到前台的时候，重新初始化pagename等信息
 @author zhaiguojun 16-10-11 in (null)
 */
- (void)handlePVEventAppInForeground {
    if (self.visibleController != nil) {
        [self onPVStart:self.visibleController];
    }
}

/**
 APP进到后台的时候，把当前pageview时间结束
 @author zhaiguojun 16-10-11 in (null)
 */
- (void)handlePVEventAppInBackgound {
    if (self.visibleController != nil) {
        [self onPVEnd:self.visibleController];
    }
}

#pragma mark - PV
- (void)onPVStart:(UIResponder<PhobosPVProtocol> *)page {
    // 必须在此处调用一下referer，因为onControllerStart
    [page initReferer];
    [page initRefererLink];
    [page initReferrerIdIfNil];
    [page initReferrerTabName];
    page.inTime = [PhobosUtil currentTime];
    page.inTimeMillis = [PhobosUtil currentMMTime];
    // 业务层更新
    if (page.updatePVStartBlock) {
        page.updatePVStartBlock();
    }

}

- (void)onPVEnd:(UIResponder<PhobosPVProtocol> *)page {
    if (![PhobosUtil isNonEmpty:page.pageName] || !page.needLogPV) {
        return;
    }
    // 业务层更新
    if (page.updatePVEndBlock) {
        page.updatePVEndBlock();
    }
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    @try {
        [dict setObject:PhobosSafeString([PhobosUtil currentTime]) forKey:@"out"];
        [dict setObject:PhobosSafeString(page.inTime) forKey:@"in"];
        [dict setObject:PhobosSafeString(page.pageName) forKey:@"page_name"];
        [dict setObject:PhobosSafeString(page.businessId) forKey:@"business_id"];
        [dict setObject:PhobosSafeString(page.referer) forKey:@"referrer"];
        [dict setObject:PhobosSafeString([PhobosUtil convertToJsonString:page.referrerLink]) forKey:@"referrer_link"];
        [dict setObject:@(0) forKey:@"fake"];
        [dict setObject:PhobosSafeString(page.referrerId) forKey:@"referrer_id"];
        [dict setObject:PhobosSafeString(page.extraParam) forKey:@"extra_param"];
        [dict setObject:PhobosSafeString(page.referrerTabName) forKey:@"referrer_tab_name"];
        [dict setObject:page.isPush.intValue ? @(page.isPush.intValue) : @(0) forKey:@"is_push"];
        [dict setObject:PhobosSafeString(page.inTimeMillis) forKey:@"in_time_millis"];
        [dict setObject:PhobosSafeString([PhobosUtil currentMMTime]) forKey:@"out_time_millis"];
        
        if (page.inTime.length > 0) {
            // 页面显示时间为空时不记录页面pv事件
            [NewPhobos track:@"page_view" attributes:dict];
        }
    }
    @catch (NSException *exception) {
        phobosLog(exception);
    }
}

/**
 *  @brief 将埋点时间封装成词典数据
 *
 *  @since 0.0.1
 */
- (NSDictionary *)prepareDictionaryForEvent:(NSString *)eventName attributes:(NSDictionary *)attributes {
    NSArray *referrerLink = _sharedClient.visibleController.referrerLink;
    // 对于埋点没有referrer_link的情况，在这里进行统一添加
    if (![attributes.allKeys containsObject:@"referrer_link"]) {
        NSMutableDictionary *attributesParams = [NSMutableDictionary dictionaryWithDictionary:attributes];
        if ([referrerLink isKindOfClass:[NSArray class]] && referrerLink.count) {
            [attributesParams setValue:referrerLink forKey:@"referrer_link"];
        } else {
            [attributesParams setValue:@[] forKey:@"referrer_link"];
        }
        attributes = attributesParams;
    }
    [self catchNullForEvent:eventName attributes:attributes];
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    @try {
        NSString *currentTime = [PhobosUtil currentTime];
        
        NSMutableDictionary *deviceParams = [NSMutableDictionary new];
        [deviceParams setValue:PhobosSafeString([[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]) forKey:@"idfa"];
        [deviceParams setValue:PhobosSafeString([[[UIDevice currentDevice] identifierForVendor] UUIDString]) forKey:@"idfv"];
        [deviceParams setValue:PhobosSafeString([PhobosUtil deviceId]) forKey:@"device_id"];
        [deviceParams setValue:@"ios" forKey:@"device_type"];
        [deviceParams setValue:@"Apple" forKey:@"manufacturer"];
        [deviceParams setValue:@(self.gps.coordinate.latitude) forKey:@"lat"];
        [deviceParams setValue:@(self.gps.coordinate.longitude) forKey:@"lng"];
        [deviceParams setValue:PhobosSafeString(_netStatus) forKey:@"is_WiFi"];
        [deviceParams setValue:[PhobosUtil getIPAddress:YES] forKey:@"ip"];
        [deviceParams setValue:PhobosSafeString(_networkStatus) forKey:@"net_type"];
        [deviceParams setValue:PhobosSafeString([PhobosUtil platform]) forKey:@"model"];
        [deviceParams setValue:@(_isGray) forKey:@"isGray"];
        [deviceParams setValue:PhobosSafeString([UIDevice currentDevice].systemVersion) forKey:@"sys_version"];
        
        NSMutableDictionary *appParams   = [NSMutableDictionary new];
        [appParams setValue:PhobosSafeString(_greyType) forKey:@"grey_type"];
        [appParams setValue:PhobosSafeString(_appName) forKey:@"name"];
        [appParams setValue:PhobosSafeString(_appVersion) forKey:@"version"];
        [appParams setValue:PhobosSafeString(_channelId) forKey:@"channel"];
        [appParams setValue:PhobosSafeString(_userType) forKey:@"user_type"];
        [appParams setValue:PhobosSafeString(_currentCityId) forKey:@"current_city_id"];
        [appParams setValue:@(_serialId++) forKey:@"serial_id"];
        
        if (_signingType == PhobosSigningTypeDebug || _signingType == PhobosSigningTypeRelease) {
            [dict setValue:@(0) forKey:@"is_release"];
        }
        NSString *nano_time = [NSString stringWithFormat:@"%lld",[[NSProcessInfo processInfo] systemUptime]];
        [dict setValue:eventName forKey:@"type"];
        [dict setValue:appParams forKey:@"app"];
        [dict setValue:sdkVersion forKey:@"version"];
        [dict setValue:deviceParams forKey:@"device"];
        [dict setValue:PhobosSafeString(_userId) forKey:@"user_id"];
        [dict setValue:PhobosSafeString(currentTime) forKey:@"create_at"];// 1584513842 当前时间（秒）
        [dict setValue:PhobosSafeString(nano_time) forKey:@"nano_time"];// 1657008897 系统启动后时长（秒）
        [dict setValue:@(mach_absolute_time()) forKey:@"absolute_time"];// 148237263697318 系统启动后的 CPU 嘀嗒数（纳秒）
        [dict setValue:PhobosSafeString([PhobosUtil currentMMTime]) forKey:@"create_at_millis"];// 1584513842753 当前时间戳（毫秒）
        [dict setValue:attributes forKey:@"params"];
        [dict setValue:PhobosSafeString(_sessionId) forKey:@"app_session_id"];
    }
    @catch (NSException *exception) {
        phobosLog(exception);
    }
    return dict;
}

#pragma mark - helpers
- (void)catchNullForEvent:(NSString *)eventName attributes:(NSDictionary *)attributes {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        @try {
            for (NSString *key in attributes.allKeys) {
                if ([attributes[key] isMemberOfClass:[NSNull class]]) {
                    if (self.captureNullExpection) {
                        self.captureNullExpection(eventName, attributes);
                    }
                    break;
                }
            }
        } @catch (NSException *exception) {
            
        }
    });
}

+ (void)onClickButtonWithAttributes:(NSDictionary *)attributes {
    [self track:@"on_cick_button" attributes:attributes];
}

+ (void)onClickButtonWithAttributes:(NSDictionary *)attributes sendNow:(BOOL)sendNow {
    [self track:@"on_cick_button" attributes:attributes sendNow:sendNow];
}

+ (void)track:(NSString *)eventName{
    [self track:eventName attributes:@{} sendNow:NO currentAPI:_sharedClient.serverAPI];
}

+ (void)track:(NSString *)eventName attributes:(NSDictionary *)attributes{
    [self track:eventName attributes:attributes sendNow:NO currentAPI:_sharedClient.serverAPI];
}

+ (void)track:(NSString *)eventName attributes:(NSDictionary *)attributes sendNow:(BOOL)sendNow{
    [self track:eventName attributes:attributes sendNow:sendNow currentAPI:_sharedClient.serverAPI];
}

+ (void)track:(NSString *)eventName currentAPI:(NSString *)currentAPI {
    [self track:eventName attributes:@{} sendNow:NO currentAPI:currentAPI];
}

+ (void)track:(NSString *)eventName attributes:(NSDictionary *)attributes currentAPI:(NSString *)currentAPI {
    [self track:eventName attributes:attributes sendNow:NO currentAPI:currentAPI];
}

+ (void)track:(NSString *)eventName attributes:(NSDictionary *)attributes sendNow:(BOOL)sendNow currentAPI:(NSString *)currentAPI {
    NSDictionary *dataDict = [_sharedClient prepareDictionaryForEvent:eventName attributes:attributes];
    @try {
        NSData *JSON = [PhobosUtil encodeJSON:dataDict];
        [PhobosDataManager insertData:dataDict sendAPI:currentAPI];
        [self disposeSendDataWithImmediately:sendNow];
    } @catch (NSException *exception) {
        NSAssert(NO, @"哎呀呀,VALUE不能为NSObject ");
    }
}

/**
 * 处理发送数据
 */
+ (void)disposeSendDataWithImmediately:(BOOL)immediately {
    NSInteger count = [PhobosDataManager fetchCountOfToBeSendEntities];
    if (immediately || count >= PhobosShardCount) {
        dispatch_semaphore_wait(_phobos_semaphore, DISPATCH_TIME_FOREVER);
        NSArray<PhobosSendDataEntity *> *entities = [PhobosDataManager fetchToBeSendDataEntities];
        [PhobosDataManager updateDataEntities:entities sendStatus:PhobosDataSendStatusSending];
        dispatch_semaphore_signal(_phobos_semaphore);
        [PhobosSendManager sendDataWithEntities:entities completion:^(NSArray<PhobosSendDataEntity *> * _Nonnull finishEntities, NSInteger code) {
            [PhobosDataManager updateDataEntities:finishEntities sendStatus:(code == 200 ? PhobosDataSendStatusFinish : PhobosDataSendStatusError)];
        }];
    }
}

@end

@implementation NewPhobos (UtilTest)

/** 获取所有非立即发送埋点数量 */
+ (NSUInteger)fetchToBeSendPhobosDataCount {
    return [PhobosDataManager fetchCountOfToBeSendEntities];
}

/** 获取待发送埋点数据, 用同步来保障异步获取数据 */
+ (NSArray *)fetchToBeSendPhobosData {
    return [PhobosDataManager fetchToBeSendDataEntities];
}

/** 清除待发送埋点数据缓存 */
+ (void)removeAllPhobosData {
    [PhobosDataManager deleteAllEntities];
}
@end
