//
//  PDNSCurlCookieStorage.m
//  PDNSCurl
//

#import "PDNSCurlCookieStorage.h"

static NSString *const kPDNSCurlCookieStorageKey = @"PDNSCurlCookieStorage";
static NSTimeInterval const kPersistenceInterval = 15.0;

@interface PDNSCurlCookieStorage ()
@property (nonatomic, strong) dispatch_queue_t persistQueue;
@property (nonatomic, strong) dispatch_source_t persistTimer;
@end

@implementation PDNSCurlCookieStorage

+ (instancetype)sharedStorage {
    static PDNSCurlCookieStorage *sharedStorage = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedStorage = [[self alloc] init];
    });
    return sharedStorage;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        // 创建用于做持久化操作的队列
        _persistQueue = dispatch_queue_create("com.alicloud.PDNScurl.cookiestorage.persist", DISPATCH_QUEUE_SERIAL);

        // 从本地加载 cookies
        [self loadPersistedCookies];

        // 设置定时器进行持久化
        [self setupPersistenceTimer];
    }
    return self;
}

- (void)dealloc {
    if (_persistTimer) {
        dispatch_source_cancel(_persistTimer);
    }
}

#pragma mark - Public Methods

- (void)setCookieWithString:(NSString *)cookieString forURL:(NSURL *)url {
    if (!cookieString.length || !url) {
        return;
    }

    // 解析 Set-Cookie 字符串并设置到 NSHTTPCookieStorage
    NSArray<NSHTTPCookie *> *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{@"Set-Cookie": cookieString} forURL:url];
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:nil];
}

- (NSString *)cookieStringForURL:(NSURL *)url {
    if (!url) {
        return nil;
    }

    NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
    NSDictionary *headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    return headers[@"Cookie"];
}

#pragma mark - Private Methods

- (void)setupPersistenceTimer {
    self.persistTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.persistQueue);
    dispatch_source_set_timer(self.persistTimer,
                            dispatch_time(DISPATCH_TIME_NOW, kPersistenceInterval * NSEC_PER_SEC),
                            kPersistenceInterval * NSEC_PER_SEC,
                            1 * NSEC_PER_SEC);

    __weak typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(self.persistTimer, ^{
        [weakSelf persistCookies];
    });

    dispatch_resume(self.persistTimer);
}

- (void)loadPersistedCookies {
    dispatch_async(self.persistQueue, ^{
        NSArray *cookiesData = [[NSUserDefaults standardUserDefaults] objectForKey:kPDNSCurlCookieStorageKey];
        if (!cookiesData) {
            return;
        }

        NSArray<NSHTTPCookie *> *cookies = [self deserializeCookies:cookiesData];
        for (NSHTTPCookie *cookie in cookies) {
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        }
    });
}

- (void)persistCookies {
    NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSArray *serializedCookies = [self serializeCookies:cookies];

    [[NSUserDefaults standardUserDefaults] setObject:serializedCookies forKey:kPDNSCurlCookieStorageKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (NSArray *)serializeCookies:(NSArray<NSHTTPCookie *> *)cookies {
    NSMutableArray *serializedCookies = [NSMutableArray array];

    for (NSHTTPCookie *cookie in cookies) {
        if ([self isCookieValid:cookie]) {
            NSDictionary *cookieProperties = [self serializableCookieProperties:cookie];
            if (cookieProperties) {
                [serializedCookies addObject:cookieProperties];
            }
        }
    }

    return [serializedCookies copy];
}

- (NSArray<NSHTTPCookie *> *)deserializeCookies:(NSArray *)serializedCookies {
    NSMutableArray<NSHTTPCookie *> *cookies = [NSMutableArray array];

    for (NSDictionary *properties in serializedCookies) {
        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:properties];
        if (cookie && [self isCookieValid:cookie]) {
            [cookies addObject:cookie];
        }
    }

    return [cookies copy];
}

- (BOOL)isCookieValid:(NSHTTPCookie *)cookie {
    NSDate *expiresDate = cookie.expiresDate;
    if (!expiresDate) {
        return YES;
    }
    return [expiresDate compare:[NSDate date]] == NSOrderedDescending;
}

- (NSDictionary *)serializableCookieProperties:(NSHTTPCookie *)cookie {
    NSMutableDictionary *properties = [NSMutableDictionary dictionary];

    properties[NSHTTPCookieName] = cookie.name;
    properties[NSHTTPCookieValue] = cookie.value;
    properties[NSHTTPCookieDomain] = cookie.domain;
    properties[NSHTTPCookiePath] = cookie.path;

    if (cookie.secure) {
        properties[NSHTTPCookieSecure] = @YES;
    }

    if (cookie.expiresDate) {
        properties[NSHTTPCookieExpires] = cookie.expiresDate;
    }

    return [properties copy];
}

@end
