//
//  AppBundleUpdate.m
//  zszyApp
//
//  Created by zr on 2022/7/4.
//

#import "AppBundleUpdate.h"
#import <React/RCTReloadCommand.h>

@implementation AppBundleUpdate

static NSString *const kPackageKey = @"appbundle";
static NSString *const bundleFile = @"index.ios.bundle";

static NSString *const kPackInfoKey = @"packInfoKey";
static NSString *const lastVersionKey = @"lastVersion";
static NSString *const currentVersionKey = @"currentVersion";
static NSString *const previousVersionKey = @"previousVersion";
static NSString *const isFirstTimeKey = @"isFirstTime";
static NSString *const isFirstLoadOkKey = @"isFirstLoadOk";

dispatch_queue_t _opQueue;

- (instancetype)init
{
    self = [super init];
    if (self) {
        _opQueue = dispatch_queue_create("cn.reactnative.appBundle", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

RCT_EXPORT_MODULE(AppBundleUpdate);

+ (NSURL *)bundleURL {
    
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *packInfo = [defaults objectForKey:kPackInfoKey];
    if (packInfo) {
        NSDictionary *curVersion = packInfo[currentVersionKey];
        BOOL isFirstTime = [packInfo[isFirstTimeKey] boolValue];
        BOOL isFirstLoadOK = [packInfo[isFirstLoadOkKey] boolValue];
        BOOL needRollback = (isFirstTime == NO && isFirstLoadOK == NO);
        if (needRollback) {
            curVersion = [AppBundleUpdate rollback];
        } else {
            if (isFirstTime) {
                NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:kPackInfoKey]];
                newInfo[isFirstTimeKey] = @(NO);
                [defaults setObject:newInfo forKey:kPackInfoKey];
                [defaults synchronize];
            }
        }
        if (curVersion) {
            NSString * version = curVersion[@"version"];
            int bundle = [curVersion[@"bundle"] intValue];
            NSString * packgePath = [AppBundleUpdate getPackageForVersion:version bundle:bundle];
            NSString * bundlePath = [packgePath stringByAppendingPathComponent:bundleFile];

            NSString * appVersion = [AppBundleUpdate appVersion];
            if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath] && [appVersion isEqualToString:version]) {
                return [NSURL fileURLWithPath:bundlePath];
            }
        }
    }

    return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
}

+ (NSDictionary *)rollback {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:kPackInfoKey]];
    newInfo[currentVersionKey] = newInfo[previousVersionKey];
    newInfo[isFirstTimeKey] = @(NO);
    newInfo[isFirstLoadOkKey] = @(YES);
    [defaults setObject:newInfo forKey:kPackInfoKey];
    [defaults synchronize];
    return newInfo[currentVersionKey];
}

+ (NSString *)appVersion {
    NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
    return version;
}

+ (NSString *)getPackageForVersion:(NSString *)version bundle:(int)bundle {
    NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    return [[[applicationSupportDirectory stringByAppendingPathComponent:kPackageKey] stringByAppendingPathComponent:version] stringByAppendingPathComponent:[NSString stringWithFormat:@"%d",bundle]];
}

+ (NSString *)getCurrentPackPath {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary * packInfo = [defaults objectForKey:kPackInfoKey];
    if (packInfo) {
        NSDictionary * bundleInfo = packInfo[currentVersionKey];
        NSString * version = bundleInfo[@"version"];
        int bundle = [bundleInfo[@"bundle"] intValue];
        NSString *packPath = [AppBundleUpdate getPackageForVersion:version bundle:bundle];
        return packPath;
    }
    return nil;
}

+ (NSDictionary *)getBundleInfoForPath:(NSString *)path {
    NSString * bundleVersionPath = [path stringByAppendingPathComponent:@"bundleVersion.json"];
    NSString *bundleVersionString = [NSString stringWithContentsOfFile:bundleVersionPath
                                                               encoding:NSUTF8StringEncoding
                                                                  error:NULL];
    if (!bundleVersionString) {
        return nil;
    }
    NSData *bundleVersionData = [bundleVersionString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:bundleVersionData
                                           options:kNilOptions
                                             error:NULL];
    return dic;
}

RCT_EXPORT_METHOD(updateBundle:(NSString *)path) {
    NSDictionary * bundleInfo = [AppBundleUpdate getBundleInfoForPath:path];
    if (!bundleInfo) {
        NSLog(@"未读取到版本信息");
        return;
    }
    NSString * appVersion = [AppBundleUpdate appVersion];
    NSString * version = bundleInfo[@"version"];
    if (![appVersion isEqualToString:version]) {
        NSLog(@"大版本不一致");
        return;
    }
    int bundle = [bundleInfo[@"bundle"] intValue];
    NSString *packPath = [AppBundleUpdate getPackageForVersion:version bundle:bundle];
    
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:kPackInfoKey]];
    newInfo[previousVersionKey] = newInfo[currentVersionKey];
    newInfo[lastVersionKey] = bundleInfo;
    newInfo[currentVersionKey] = bundleInfo;
    newInfo[isFirstTimeKey] = @(YES);
    newInfo[isFirstLoadOkKey] = @(NO);
    [defaults setObject:newInfo forKey:kPackInfoKey];
    [defaults synchronize];
    
    // 复制新包
    [[NSFileManager defaultManager] createDirectoryAtPath:packPath withIntermediateDirectories:YES attributes:nil error:nil];
    [[NSFileManager defaultManager] removeItemAtPath:packPath error:nil];
    [[NSFileManager defaultManager] copyItemAtPath:path toPath:packPath error:nil];
    [self loadBundle];
}

RCT_EXPORT_METHOD(markSuccess)
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:kPackInfoKey]];
    newInfo[isFirstTimeKey] = @(NO);
    newInfo[isFirstLoadOkKey] = @(YES);
    [defaults setObject:newInfo forKey:kPackInfoKey];
    [defaults synchronize];
    [self clearInvalidFiles];
}

// 新增获取最后版本方法
RCT_EXPORT_METHOD(getLastVersion:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *packInfo = [defaults objectForKey:kPackInfoKey];
    if (packInfo) {
        NSDictionary *lastVersion = packInfo[lastVersionKey];
        if (lastVersion) {
            resolve(lastVersion);
            return;
        }
    }
    resolve([NSNull null]);
}

- (void)loadBundle {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSString * packgePath = [AppBundleUpdate getCurrentPackPath];
        if (packgePath) {
            NSString * bundlePath = [packgePath stringByAppendingPathComponent:bundleFile];
            NSURL * url = [NSURL fileURLWithPath:bundlePath];
            if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) {
                [super.bridge setValue:url forKey:@"bundleURL"];
                RCTTriggerReloadCommandListeners(@"updateBundle");
            }
        }
    });
}

- (void)clearInvalidFiles
{
    dispatch_async(_opQueue, ^{
        NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString * downloadDir = [applicationSupportDirectory stringByAppendingPathComponent:kPackageKey];
        NSError * error;
        NSArray *list = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:downloadDir error:&error];
        if (error) {
            return;
        }
        NSString * appVersion = [AppBundleUpdate appVersion];
        for (NSString *fileName in list) {
            NSString * versionPath = [downloadDir stringByAppendingPathComponent:fileName];
            if (![fileName isEqualToString:appVersion]) {
                [[NSFileManager defaultManager] removeItemAtPath:versionPath error:nil];
            }
        }
    });
}

- (NSArray<NSString *> *)supportedEvents {
    return @[];
}

@end
