//
//  EGIAPKeyChainStore.m
//  EGirl
//
//  Created by donglyu on 2020/6/24.
//  Copyright © 2020 EGirl. All rights reserved.
//

#import "EGIAPKeyChainStore.h"
#import <pthread.h>
#import "MJExtension.h"
@interface EGIAPKeyChainStore ()

@property (nonatomic) pthread_mutex_t lock;

@end

@implementation EGIAPKeyChainStore

- (instancetype)initWithService:(NSString *)service Group:(NSString *)group {
    EGIAPKeyChainStore *store = [super initWithService:service accessGroup:group];
    pthread_mutex_init(&(_lock), NULL);
    // ⚠️移除所有持久化数据 for test
//     [self removeItemForKey:KeychainServiceKey];
    return store;
}

- (void)dealloc {
    pthread_mutex_destroy(&_lock);
}

#pragma mark - public

- (void)SavePaymentTransactionModel:(NSArray <PaymentTransactionModel *> *)models forUserId:(NSInteger)userId {
    if (models.count == 0) {
        return;
    }

    // 将 models 归档.
    NSMutableSet<NSData *> *modelsDataSetM = [NSMutableSet setWithArray:[self internalEncodeModels:models]];

    // 与已有的数据组合存储.
    NSMutableArray<PaymentTransactionModel *> *modelsExisted = [self FetchAllTransactionModelsForUser:userId error:nil];

    pthread_mutex_lock(&_lock);
    if (modelsExisted.count) { // 本地已持久化的一些
        // 检查一下 keychain 中是否已经存在当前 model.
        NSMutableArray *modelsNeedToRemoveExisted = [@[] mutableCopy];
        for (PaymentTransactionModel *modelExisted in modelsExisted) {
            for (PaymentTransactionModel *model in models) {
                if ([modelExisted.tradeNo isEqualToString:model.tradeNo]) {
                    [modelsNeedToRemoveExisted addObject:modelExisted];
                    NSLog(@"keychain 中已经有: %@, 不用再存一遍.", model);
                }
            }
        }
        if (modelsNeedToRemoveExisted.count) {
            [modelsExisted removeObjectsInArray:modelsNeedToRemoveExisted];
        }
        if (modelsExisted.count) {
            NSMutableArray<NSData *> *modelsExistedDataM = [self internalEncodeModels:modelsExisted];
            [modelsDataSetM addObjectsFromArray:modelsExistedDataM];
        }
    }

    // 存入 keychain.
    [self internalSaveModelsData:modelsDataSetM.copy forUser:userId];
    pthread_mutex_unlock(&_lock);
}

- (NSMutableArray <PaymentTransactionModel *> *)FetchAllTransactionModelsForUser:(NSInteger)userId error:(NSError *__autoreleasing _Nullable *)error {
    pthread_mutex_lock(&_lock);
    NSError *fetchError;
    NSData *data = [self dataForKey:KeychainServiceKey error:&fetchError];
    if (fetchError) {
        NSLog(@"⚠️get keychain data error: %@", fetchError);
    }
    pthread_mutex_unlock(&_lock);

    if (data == nil) {
        //DLog(@"iap keychain data is nil");
        return nil;
    }

    pthread_mutex_lock(&_lock);
    NSDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    pthread_mutex_unlock(&_lock);

    NSMutableArray<NSData *> *modelsData = nil;
    if (![dict.allKeys containsObject:[NSString stringWithFormat:@"%ld", (long)userId]]) {
        NSLog(@"iap keychain user's data is nil");
        return nil;
    }

    pthread_mutex_lock(&_lock);
    NSString *userIdStr = [NSString stringWithFormat:@"%ld", (long)userId];
    modelsData = [NSKeyedUnarchiver unarchiveObjectWithData:[dict valueForKey:userIdStr]];
    pthread_mutex_unlock(&_lock);

    pthread_mutex_lock(&_lock);
    NSMutableArray<PaymentTransactionModel *> *arrM = [NSMutableArray arrayWithCapacity:modelsData.count];
    for (NSData *data in modelsData) {
        NSParameterAssert([data isKindOfClass:[NSData class]]);
        PaymentTransactionModel *model = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        if (model) {
            [arrM addObject:model];
        }
    }
    pthread_mutex_unlock(&_lock);

    NSLog(@"💰💰💰 已持久化的数据数: %ld个 分别是:", (long)arrM.count);

#ifdef DEBUG
    for (PaymentTransactionModel *m in arrM) {
        NSLog(@"🛒tradeId: %@  🧰Id: %@ time:%@",  m.tradeNo, m.transactionId, @(m.tradeTime));
    }

#endif
    return [arrM mutableCopy];
}

- (void)DeletePaymentTransactionModelWithTransactionId:(NSString *)transactionId forUserId:(NSInteger)userId {
    if (transactionId.length == 0) {
        NSAssert(false, @"transactionId is nil");
        return;
    }

    NSError *error;
    NSMutableArray *modelsM = [self FetchAllTransactionModelsForUser:userId error:&error].mutableCopy;
    NSLog(@"✅✅✅✅✅ 票据验证 • 大成功 ✅✅✅✅✅");
    [modelsM.reverseObjectEnumerator.allObjects enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
        PaymentTransactionModel *per = obj;
        if ([per.transactionId isEqualToString:transactionId]) {
            NSLog(@"移除持久化 transactionId:%@ tradeNo:%@", per.transactionId, per.tradeNo);
            [modelsM removeObject:per];
        }
    }];

    NSMutableArray<NSData *> *modelsExistedDataM = [self internalEncodeModels:modelsM];

    [self internalSaveModelsData:modelsExistedDataM forUser:userId];
}

- (PaymentTransactionModel *)FindPaymentTransactionModelWithTransactionId:(NSString *)transactionId forUserId:(NSInteger)userId {
    if (transactionId.length == 0) {
        NSAssert(false, @"transactionId is nil");
        return nil;
    }

    NSError *error;
    NSMutableArray *modelsM = [self FetchAllTransactionModelsForUser:userId error:&error].mutableCopy;

    for (PaymentTransactionModel *per in modelsM) {
        if ([per.transactionId isEqualToString:transactionId]) {
            return per;
            break;
        }
    }

    return [PaymentTransactionModel new];
}

#pragma mark 预缓存product和trasaction part

- (void)StorePreProduct:(NSString *)productId TradeId:(NSString *)tradeId {
    if (productId.length == 0 && tradeId.length == 0) {
        NSLog(@"❌ prestore product:%@ tradeId: %@", productId, tradeId);
        return;
    }

    pthread_mutex_lock(&_lock);
    NSData *data = [self dataForKey:KeychainServicePreStoreKey];
    pthread_mutex_unlock(&_lock);

    NSData *finalData = nil;

    if (data == nil) {
        NSDictionary *dict = @{ productId: tradeId };
        finalData = [dict mj_JSONData];
        DLog(@"pre store data:%@", finalData);
    } else {
        NSMutableDictionary *dict = [[data mj_JSONObject] mutableCopy];
        DLog(@"💰keychain中持久的product和tradeid  dict: %@", dict);
        [dict setObject:tradeId forKey:productId];
        finalData = [dict mj_JSONData];
    }

    if (finalData) {
        NSError *error;
        pthread_mutex_lock(&_lock);
        [self setData:finalData forKey:KeychainServicePreStoreKey];
        pthread_mutex_unlock(&_lock);
        if (error) {
            NSLog(@"pre store error:%@", error.localizedDescription);
        }
    } else {
        NSAssert(false, @"");
    }
}

- (BOOL)FindAndMatchTradeToStartVerifyByProudctId:(NSString *)productId BindTransactionId:(NSString *)transactionId ForUserId:(NSInteger)userId {
    NSString *tradeId = [self getTradeIdBy:productId];
    if (tradeId.length == 0) {
        return NO;
    }

    PaymentTransactionModel *model = [[PaymentTransactionModel alloc] init];
    model.tradeNo = tradeId;
    model.transactionId = transactionId;
    model.productId = productId;
    model.userId = userId;
    model.tradeTime = ([[NSDate date] timeIntervalSince1970] * 1000);

    [self SavePaymentTransactionModel:@[model] forUserId:userId];

    return YES;
}

#pragma mark - events

/// 清理所有空transactionId的数据
- (void)ClearAllEmptyTransactionIdData:(NSInteger)userId {
    NSError *error;
    NSMutableArray *modelsM = [self FetchAllTransactionModelsForUser:userId error:&error].mutableCopy;
    NSMutableArray *circle = modelsM.mutableCopy;

    __block BOOL isOperted = NO;
    [circle.reverseObjectEnumerator.allObjects enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
        PaymentTransactionModel *per = obj;
        if (per.transactionId.length == 0) {
            isOperted = YES;
            [modelsM removeObject:per];
        }
    }];

    if (isOperted) {
        NSMutableArray<NSData *> *modelsExistedDataM = [self internalEncodeModels:modelsM];
        if (modelsM.count) {
            NSLog(@"--- 💰💰Clear All Empty transactionId data done!💰💰 ---");
            for (PaymentTransactionModel *m in modelsM) {
                DLog(@"🏥tradeId: %@  🍂transactionId: %@ ",  m.tradeNo, m.transactionId);
            }
        }

        [self internalSaveModelsData:modelsExistedDataM forUser:userId];
    }
}

#pragma mark pre store

- (NSString *)getTradeIdBy:(NSString *)productId {
    if (productId.length == 0) {
        return nil;
    }

    pthread_mutex_lock(&_lock);
    NSData *data = [self dataForKey:KeychainServicePreStoreKey];
    pthread_mutex_unlock(&_lock);

    NSDictionary *dict = [data mj_JSONObject];
    if (dict && [dict objectForKey:productId]) {
        NSString *tradeId = [dict objectForKey:productId];
        return tradeId;
    }

    return nil;
}

#pragma mark - private

/// 保存某个用户的 交易验证数据
- (void)internalSaveModelsData:(nullable NSArray <NSData *> *)modelsData forUser:(NSInteger)userId {
    NSData *setData = modelsData.count ? [NSKeyedArchiver archivedDataWithRootObject:modelsData] : nil; // mj_jsonData

    // 原本数据
    NSData *dictData = [self dataForKey:KeychainServiceKey];

    // -> NSDictionary
    NSMutableDictionary *dictM;
    if (dictData) {
        dictM = [NSKeyedUnarchiver unarchiveObjectWithData:dictData];
    }
    if (!dictM) {
        dictM = [NSMutableDictionary dictionary];
    }

    NSString *userIdStr = [NSString stringWithFormat:@"%ld", (long)userId];
    if (setData) {
        [dictM setObject:setData forKey:userIdStr];
    } else { //  清空某个userId的数据
        if ([dictM.allKeys containsObject:userIdStr]) {
            [dictM removeObjectForKey:userIdStr];
        }
    }

    NSData *data = dictM.count ? [NSKeyedArchiver archivedDataWithRootObject:dictM] : nil;    // [NSKeyedArchiver archivedDataWithRootObject:dictM]

    NSLog(@"💰💰💰 当前持久化的票据: %@", dictM);

    // 先删除, 后存储.
    [self removeItemForKey:KeychainServiceKey];
    if (data) {
        NSError *error;
        [self setData:data forKey:KeychainServiceKey error:&error];
        if (error) {
            DLog(@"💰❌ save models to keychain error: %@", error);
        }
    }
}

- (NSMutableArray<NSData *> *)internalEncodeModels:(NSArray<PaymentTransactionModel *> *)models {
    NSMutableArray *modelsDataM = [NSMutableArray array];
    for (PaymentTransactionModel *model in models) {
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model];
        NSParameterAssert(data);
        if (data) {
            [modelsDataM addObject:data];
        }
    }
    return modelsDataM;
}

@end
