// ─────────────────────────────────────────────────────────────────────────────
// `TQNavigationDelegate` 导航决策(URL 路由优先级)单测。
//
// 测试策略:由于 `WKNavigationAction` 无法直接构造(Apple 未开放 init),
// 直接测试 `TQSchemeInterceptor` + `TQExternalSchemeHandler` 的组合路由,
// 模拟 `TQNavigationDelegate` 内部的决策流程。
// ─────────────────────────────────────────────────────────────────────────────

#import <XCTest/XCTest.h>
#import "TQSchemeInterceptor.h"
#import "TQSdkEvent.h"
#import "TQSignResult.h"

typedef NS_ENUM(NSInteger, TQNavigationDecision) {
    TQNavigationDecisionAllow  = 0,
    TQNavigationDecisionCancel = 1,
};

@interface TQNavigationDelegateTests : XCTestCase
@property (nonatomic, strong) NSMutableArray<id<TQSdkEvent>> *events;
@property (nonatomic, strong) NSMutableArray<TQSignResult *> *signResults;
@end

@implementation TQNavigationDelegateTests

- (void)setUp {
    [super setUp];
    self.events = [NSMutableArray array];
    self.signResults = [NSMutableArray array];
}

/// 模拟 `TQNavigationDelegate.decidePolicyForNavigationAction` 的核心路由逻辑
/// (qianapp → http(s) → external → 兜底)
- (TQNavigationDecision)simulateDecisionWithUrl:(nullable NSString *)urlString
                                externalSchemes:(NSArray<NSString *> *)externalSchemes {
    if (urlString.length == 0) return TQNavigationDecisionCancel;
    NSURL *url = [NSURL URLWithString:urlString];
    if (url == nil) return TQNavigationDecisionCancel;

    // 1. qianapp
    __weak __typeof(self) weakSelf = self;
    TQSchemeInterceptor *schemeInterceptor =
        [[TQSchemeInterceptor alloc] initWithOnSignResult:^(TQSignResult *result) {
            [weakSelf.signResults addObject:result];
        } onEvent:^(id<TQSdkEvent> event) {
            [weakSelf.events addObject:event];
        }];
    if ([schemeInterceptor handle:urlString]) {
        return TQNavigationDecisionCancel;
    }

    // 2. http(s) 走默认
    NSString *scheme = [url.scheme lowercaseString] ?: @"";
    if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) {
        return TQNavigationDecisionAllow;
    }

    // 3 & 4. external + 兜底
    TQExternalSchemeHandler *externalHandler =
        [[TQExternalSchemeHandler alloc] initWithExternalSchemes:externalSchemes
                                                          onEvent:^(id<TQSdkEvent> event) {
            [weakSelf.events addObject:event];
        }];
    if ([externalHandler tryOpen:urlString]) {
        return TQNavigationDecisionCancel;
    }

    return TQNavigationDecisionCancel;
}

- (TQNavigationDecision)simulateDecisionWithUrl:(nullable NSString *)urlString {
    return [self simulateDecisionWithUrl:urlString
                          externalSchemes:@[@"weixin", @"alipays"]];
}

#pragma mark - 优先级 1: qianapp:// → 拦截

- (void)test_qianapp_url_is_intercepted {
    TQNavigationDecision d = [self simulateDecisionWithUrl:
                              @"qianapp://callback/sign?action=sign&result=success&flowId=abc"];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
    // SchemeInterceptor 异步派发 onSignResult,但 handle 同步返回 YES
}

- (void)test_qianapp_uppercase_is_intercepted {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"QIANAPP://x?action=fill"];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
}

#pragma mark - 优先级 2: http(s) → 放行

- (void)test_http_url_is_allowed {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"http://example.com/page"];
    XCTAssertEqual(d, TQNavigationDecisionAllow);
    XCTAssertEqual(self.events.count, 0u);
}

- (void)test_https_url_is_allowed {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"https://quick.qian.tencent.cn/sign"];
    XCTAssertEqual(d, TQNavigationDecisionAllow);
    XCTAssertEqual(self.events.count, 0u);
}

- (void)test_https_uppercase_is_allowed {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"HTTPS://EXAMPLE.COM/PATH"];
    XCTAssertEqual(d, TQNavigationDecisionAllow);
}

#pragma mark - 优先级 3: externalSchemes 白名单 → cancel

- (void)test_weixin_in_whitelist_is_cancelled {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"weixin://wxa/launchminiprogram?appid=wx123"
                                            externalSchemes:@[@"weixin", @"alipays"]];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
}

- (void)test_alipays_in_whitelist_is_cancelled {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"alipays://platformapi/startapp?appId=123"
                                            externalSchemes:@[@"weixin", @"alipays"]];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
}

#pragma mark - 优先级 4: 未知 scheme → cancel + schemeNotMatched

- (void)test_unknown_scheme_emits_schemeNotMatched {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"mqqapi://chat?uin=12345"
                                            externalSchemes:@[@"weixin", @"alipays"]];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
    XCTAssertEqual(self.events.count, 1u);
    id evt = self.events.firstObject;
    XCTAssertTrue([evt isKindOfClass:[TQSchemeNotMatchedEvent class]], @"应发出 schemeNotMatched 事件");
    if ([evt isKindOfClass:[TQSchemeNotMatchedEvent class]]) {
        XCTAssertEqualObjects(((TQSchemeNotMatchedEvent *)evt).url, @"mqqapi://chat?uin=12345");
    }
}

#pragma mark - 边界: nil / 空 → cancel

- (void)test_nil_url_returns_cancel {
    TQNavigationDecision d = [self simulateDecisionWithUrl:nil];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
    XCTAssertEqual(self.events.count, 0u);
}

- (void)test_empty_url_returns_cancel {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@""];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
    XCTAssertEqual(self.events.count, 0u);
}

#pragma mark - 优先级验证: qianapp 优先于 externalSchemes

- (void)test_qianapp_takes_priority_over_externalSchemes {
    // 即使 externalSchemes 包含 qianapp(防御性测试)
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"qianapp://callback?action=sign&result=success"
                                            externalSchemes:@[@"qianapp", @"weixin"]];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
    // SchemeInterceptor.handle 同步返回 YES,不会进 ExternalSchemeHandler
}

#pragma mark - 优先级验证: http 优先于 externalSchemes

- (void)test_http_takes_priority_over_externalSchemes {
    // 即使 externalSchemes 包含 http(防御性测试)
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"http://example.com"
                                            externalSchemes:@[@"http", @"weixin"]];
    XCTAssertEqual(d, TQNavigationDecisionAllow, @"http 应放行,不走 externalSchemes");
}

#pragma mark - scheme 大小写不敏感

- (void)test_external_scheme_case_insensitive {
    TQNavigationDecision d = [self simulateDecisionWithUrl:@"WEIXIN://wxa/test"
                                            externalSchemes:@[@"weixin"]];
    XCTAssertEqual(d, TQNavigationDecisionCancel);
}

@end
