#import "TQWebViewPrewarmer.h"
#import "TQManifestStore.h"
#import "TQPrewarmDedup.h"
#import "TQWebViewPool.h"
#import "TQWarmupManifest.h"
#import <WebKit/WebKit.h>

@implementation TQWebViewPrewarmer

static TQManifestStore *sStore = nil;
static TQPrewarmDedup *sDedup = nil;
static TQPrewarmConfig *sConfig = nil;
static BOOL sConfigured = NO;
static NSObject *sLock = nil;

+ (void)initialize {
    if (self == [TQWebViewPrewarmer class]) {
        sStore = [[TQManifestStore alloc] init];
        sDedup = [[TQPrewarmDedup alloc] init];
        sConfig = [TQPrewarmConfig defaultConfig];
        sLock = [[NSObject alloc] init];
    }
}

+ (void)configure:(nullable TQPrewarmConfig *)config {
    TQPrewarmConfig *cfg = [(config ?: [TQPrewarmConfig defaultConfig]) copy];
    if (cfg.poolSize > 1) {
        NSLog(@"[TQWebViewPrewarmer] poolSize > 1 not supported in v1, clamped to 1");
        cfg.poolSize = 1;
    }
    @synchronized (sLock) {
        sConfig = cfg;
        sConfigured = YES;
    }
    [[TQWebViewPool sharedPool] setEnabled:cfg.enablePool];
    // 内存告警释放预热实例后重置去重状态，使后续 prewarm 可重新预热（与 Android/HarmonyOS 对齐）。
    [TQWebViewPool sharedPool].onReleasedByMemoryWarning = ^{
        [sDedup reset];
    };

    NSString *url = (cfg.manifestUrl.length > 0)
        ? cfg.manifestUrl
        : [self manifestUrlForEnvironment:cfg.environment];
    [sStore setManifestUrl:url];
    sStore.onVersionChanged = ^{
        [sDedup reset];
    };
    [sStore ensureFresh:nil];
}

+ (void)prewarm:(TQPrewarmLevel)level bizType:(nullable NSString *)bizType {
    if (![self isConfigured]) {
        [self lazyInit];
    }
    // bizType 允许为空：空时预热公共资源（warmup.html 加载公共部分），去重按空 key 记账
    NSString *bt = bizType ?: @"";
    [sStore ensureFresh:^(TQWarmupManifest *manifest) {
        if (![sDedup shouldRun:level bizType:bt]) {
            return;
        }
        [self execute:level bizType:bt manifest:manifest];
        [sDedup markDone:level bizType:bt];
    }];
}

+ (void)releasePooled {
    [[TQWebViewPool sharedPool] releasePool];
}

+ (void)releaseAll {
    [[TQWebViewPool sharedPool] releasePool];
    [sDedup reset];
}

+ (void)warnIfEnvironmentMismatch:(nullable NSString *)url {
    if (![self isConfigured]) {
        return;
    }
    NSNumber *env = [self environmentForURL:url];
    if (env == nil) {
        return;
    }
    TQPrewarmEnvironment configured;
    @synchronized (sLock) { configured = sConfig.environment; }
    if (env.integerValue != configured) {
        NSLog(@"[TQWebViewPrewarmer] environment mismatch: configured=%ld but open() host indicates %ld — prewarm cache may miss",
              (long)configured, (long)env.integerValue);
    }
}

+ (void)getMenuBizTypes:(void (^)(NSArray<NSString *> *bizTypes))callback {
    if (![self isConfigured]) {
        [self lazyInit];
    }
    [sStore ensureFresh:^(TQWarmupManifest *manifest) {
        NSArray<NSString *> *menu = manifest.menuBizTypes;
        callback(menu ?: manifest.bizTypes.allKeys);
    }];
}

#pragma mark - 映射

+ (NSString *)manifestUrlForEnvironment:(TQPrewarmEnvironment)env {
    switch (env) {
        case TQPrewarmEnvironmentBeta:
            return @"https://res.ess.tencent.cn/cdn/h5-std-beta/warmup-manifest.json";
        case TQPrewarmEnvironmentTest:
            return @"https://res.ess.tencent.cn/cdn/h5-std-dev/warmup-manifest.json";
        case TQPrewarmEnvironmentProd:
        default:
            return @"https://res.ess.tencent.cn/cdn/h5-std/warmup-manifest.json";
    }
}

+ (nullable NSNumber *)environmentForURL:(nullable NSString *)url {
    if (url.length == 0) {
        return nil;
    }
    NSString *host = [[NSURL URLWithString:url] host].lowercaseString;
    if ([host isEqualToString:@"quick.qian.tencent.com"]) {
        return @(TQPrewarmEnvironmentProd);
    }
    if ([host isEqualToString:@"quick.beta.qian.tencent.cn"]) {
        return @(TQPrewarmEnvironmentBeta);
    }
    if ([host isEqualToString:@"quick.test.qian.tencent.cn"]) {
        return @(TQPrewarmEnvironmentTest);
    }
    return nil;
}

#pragma mark - 内部

+ (BOOL)isConfigured {
    @synchronized (sLock) { return sConfigured; }
}

+ (void)lazyInit {
    TQPrewarmEnvironment env;
    @synchronized (sLock) {
        sConfigured = YES;
        env = sConfig.environment;
    }
    [sStore setManifestUrl:[self manifestUrlForEnvironment:env]];
    sStore.onVersionChanged = ^{
        [sDedup reset];
    };
    // 内存告警释放预热实例后重置去重状态（与 configure 一致，覆盖懒初始化路径）。
    [TQWebViewPool sharedPool].onReleasedByMemoryWarning = ^{
        [sDedup reset];
    };
}

+ (void)execute:(TQPrewarmLevel)level bizType:(NSString *)bizType manifest:(TQWarmupManifest *)manifest {
    // L1：DNS 预解析全局 hosts（所有档位地基）
    [self prefetchDns:manifest.hosts];

    if (level == TQPrewarmLevelMinimal) {
        return;
    }
    TQWarmupBizType *biz = manifest.bizTypes[bizType];
    if (biz == nil) {
        return;
    }
    BOOL injectUA = YES;
    NSString *uaExtra = @"";
    NSString *lang = nil;
    @synchronized (sLock) {
        injectUA = sConfig.injectUserAgent;
        uaExtra = sConfig.userAgentExtra ?: @"";
        lang = sConfig.preferredLanguage;
    }
    if (level == TQPrewarmLevelLight) {
        // 离屏预建空实例（触发 WebContent 进程启动 + 保活），按 UA 配置建；iOS 无轻量 L2/L3
        [[TQWebViewPool sharedPool] preCreateWithInjectUserAgent:injectUA userAgentExtra:uaExtra];
    } else if (level == TQPrewarmLevelHeavy) {
        // 预热实例加载 warmup.html（L4，写入共享磁盘缓存跨实例命中）；不复用到页面
        [[TQWebViewPool sharedPool] warmWithUrl:[self appendLang:biz.warmupUrl lang:lang]
                                injectUserAgent:injectUA
                                 userAgentExtra:uaExtra];
    }
}

/// 将语言偏好拼进 warmup.html URL（?lang=，warmup.html 内优先级最高）；空则原样返回。
+ (NSString *)appendLang:(NSString *)url lang:(nullable NSString *)lang {
    if (lang.length == 0) {
        return url;
    }
    NSString *sep = [url containsString:@"?"] ? @"&" : @"?";
    NSString *encoded = [lang stringByAddingPercentEncodingWithAllowedCharacters:
                         [NSCharacterSet URLQueryAllowedCharacterSet]] ?: lang;
    return [NSString stringWithFormat:@"%@%@lang=%@", url, sep, encoded];
}

+ (void)prefetchDns:(NSArray<NSString *> *)hosts {
    for (NSString *h in hosts) {
        NSString *host = [self hostOf:h];
        if (host.length == 0) {
            continue;
        }
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
            CFHostRef ref = CFHostCreateWithName(NULL, (__bridge CFStringRef)host);
            if (ref) {
                CFHostStartInfoResolution(ref, kCFHostAddresses, NULL);
                CFRelease(ref);
            }
        });
    }
}

+ (NSString *)hostOf:(NSString *)urlOrHost {
    if ([urlOrHost containsString:@"://"]) {
        NSString *h = [[NSURL URLWithString:urlOrHost] host];
        return h ?: @"";
    }
    NSString *s = urlOrHost;
    NSRange slash = [s rangeOfString:@"/"];
    if (slash.location != NSNotFound) {
        s = [s substringToIndex:slash.location];
    }
    NSRange colon = [s rangeOfString:@":"];
    if (colon.location != NSNotFound) {
        s = [s substringToIndex:colon.location];
    }
    return s;
}

@end
