// ─────────────────────────────────────────────────────────────────────────────
// `TQExternalSchemeHandler.tryOpen:` 单测。
//
// 由于模拟器上 `[UIApplication canOpenURL:]` 对微信/支付宝永远返回 NO,
// 无法覆盖"成功打开"路径,因此本测试只验证返回值与不可观测白盒事件之外
// 可同步观测的事件流(未匹配 → schemeNotMatched)。
// ─────────────────────────────────────────────────────────────────────────────

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

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

@implementation TQExternalSchemeHandlerTests

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

- (TQExternalSchemeHandler *)makeHandlerWithSchemes:(NSArray<NSString *> *)schemes {
    __weak __typeof(self) weakSelf = self;
    return [[TQExternalSchemeHandler alloc] initWithExternalSchemes:schemes
                                                            onEvent:^(id<TQSdkEvent> event) {
        [weakSelf.events addObject:event];
    }];
}

- (TQExternalSchemeHandler *)makeHandler {
    return [self makeHandlerWithSchemes:@[@"weixin", @"alipays"]];
}

#pragma mark - 白名单未命中 → schemeNotMatched

- (void)test_unknown_scheme_emits_schemeNotMatched_and_returns_true {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[@"weixin", @"alipays"]];
    BOOL result = [handler tryOpen:@"mqqapi://chat?uin=12345"];

    XCTAssertTrue(result, @"未知 scheme 应被消费(丢弃)");
    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");
    }
}

- (void)test_custom_scheme_not_in_whitelist_emits_schemeNotMatched {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[@"weixin"]];
    BOOL result = [handler tryOpen:@"tbopen://m.taobao.com/page"];

    XCTAssertTrue(result);
    XCTAssertEqual(self.events.count, 1u);
    XCTAssertTrue([self.events.firstObject isKindOfClass:[TQSchemeNotMatchedEvent class]]);
}

#pragma mark - 白名单命中(模拟器上 canOpenURL 返回 NO,所以实际仍走 schemeNotMatched 分支)

- (void)test_weixin_in_whitelist_returns_true {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[@"weixin", @"alipays"]];
    BOOL result = [handler tryOpen:@"weixin://wxa/launchminiprogram?appid=wx123"];

    // 模拟器 canOpenURL=NO,但 tryOpen 仍返回 YES(已消费)
    XCTAssertTrue(result, @"白名单命中应返回 YES");
}

- (void)test_alipays_in_whitelist_returns_true {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[@"weixin", @"alipays"]];
    BOOL result = [handler tryOpen:@"alipays://platformapi/startapp?appId=123"];

    XCTAssertTrue(result);
}

#pragma mark - 大小写不敏感

- (void)test_scheme_matching_is_case_insensitive {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[@"weixin"]];
    BOOL result = [handler tryOpen:@"WEIXIN://wxa/test"];

    XCTAssertTrue(result, @"scheme 匹配应大小写不敏感");
}

- (void)test_whitelist_uppercase_matches_lowercase_url {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[@"WEIXIN"]];
    BOOL result = [handler tryOpen:@"weixin://wxa/test"];

    XCTAssertTrue(result);
}

#pragma mark - http(s) → 不处理

- (void)test_http_returns_false {
    TQExternalSchemeHandler *handler = [self makeHandler];
    BOOL result = [handler tryOpen:@"http://example.com"];

    XCTAssertFalse(result, @"http 不归 ExternalSchemeHandler 处理");
    XCTAssertEqual(self.events.count, 0u);
}

- (void)test_https_returns_false {
    TQExternalSchemeHandler *handler = [self makeHandler];
    BOOL result = [handler tryOpen:@"https://example.com/page"];

    XCTAssertFalse(result);
    XCTAssertEqual(self.events.count, 0u);
}

#pragma mark - qianapp → 防御性兜底

- (void)test_qianapp_returns_false {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[@"qianapp", @"weixin"]];
    BOOL result = [handler tryOpen:@"qianapp://callback?action=sign"];

    XCTAssertFalse(result, @"qianapp 应由 SchemeInterceptor 处理,此处兜底返回 NO");
    XCTAssertEqual(self.events.count, 0u);
}

#pragma mark - nil / 空 → 返回 NO

- (void)test_nil_url_returns_false {
    TQExternalSchemeHandler *handler = [self makeHandler];
    BOOL result = [handler tryOpen:nil];

    XCTAssertFalse(result);
    XCTAssertEqual(self.events.count, 0u);
}

- (void)test_empty_url_returns_false {
    TQExternalSchemeHandler *handler = [self makeHandler];
    BOOL result = [handler tryOpen:@""];

    XCTAssertFalse(result);
    XCTAssertEqual(self.events.count, 0u);
}

#pragma mark - 无效 URL → 不崩溃

- (void)test_invalid_url_returns_false {
    TQExternalSchemeHandler *handler = [self makeHandler];
    // 空格在 URL 中非法
    BOOL result = [handler tryOpen:@"weixin://invalid url with spaces"];
    // [NSURL URLWithString:] 行为取决于 Foundation 版本,但不应崩溃
    (void)result;
}

#pragma mark - 空白名单

- (void)test_empty_whitelist_always_emits_schemeNotMatched {
    TQExternalSchemeHandler *handler = [self makeHandlerWithSchemes:@[]];
    BOOL result = [handler tryOpen:@"weixin://wxa/test"];

    XCTAssertTrue(result, @"非 http/qianapp 的 scheme 都应被消费");
    XCTAssertEqual(self.events.count, 1u);
    XCTAssertTrue([self.events.firstObject isKindOfClass:[TQSchemeNotMatchedEvent class]]);
}

@end
