import XCTest
@testable import TencentQianWebViewSDK

/// `ExternalSchemeHandler.tryOpen` 单测。
///
/// 由于模拟器上 `UIApplication.shared.canOpenURL` 对微信/支付宝永远返回 false,
/// 无法覆盖"成功打开"路径,因此通过 Protocol 抽象来测试核心逻辑。
///
/// 覆盖:
/// - 白名单命中 → 返回 true + 事件
/// - 白名单未命中 → 返回 true + schemeNotMatched
/// - http(s) → 返回 false(不归本 handler 处理)
/// - qianapp → 返回 false(防御性兜底)
/// - nil / 空 → 返回 false
/// - 大小写不敏感匹配
final class ExternalSchemeHandlerTests: XCTestCase {

    private var events: [SdkEvent] = []

    override func setUp() {
        super.setUp()
        events = []
    }

    private func makeHandler(schemes: [String] = ["weixin", "alipays"]) -> ExternalSchemeHandler {
        ExternalSchemeHandler(externalSchemes: schemes, onEvent: { [weak self] event in
            self?.events.append(event)
        })
    }

    // MARK: - 白名单未命中 → schemeNotMatched

    func test_unknown_scheme_emits_schemeNotMatched_and_returns_true() {
        let handler = makeHandler(schemes: ["weixin", "alipays"])
        let result = handler.tryOpen("mqqapi://chat?uin=12345")

        XCTAssertTrue(result, "未知 scheme 应被消费(丢弃)")
        XCTAssertEqual(events.count, 1)
        if case .schemeNotMatched(let url) = events.first {
            XCTAssertEqual(url, "mqqapi://chat?uin=12345")
        } else {
            XCTFail("应发出 schemeNotMatched 事件")
        }
    }

    func test_custom_scheme_not_in_whitelist_emits_schemeNotMatched() {
        let handler = makeHandler(schemes: ["weixin"])
        let result = handler.tryOpen("tbopen://m.taobao.com/page")

        XCTAssertTrue(result)
        XCTAssertEqual(events.count, 1)
        if case .schemeNotMatched = events.first {} else {
            XCTFail("应发出 schemeNotMatched 事件")
        }
    }

    // MARK: - 白名单命中(模拟器上 canOpenURL 返回 false,所以实际会走 schemeNotMatched 分支)

    func test_weixin_in_whitelist_returns_true() {
        let handler = makeHandler(schemes: ["weixin", "alipays"])
        let result = handler.tryOpen("weixin://wxa/launchminiprogram?appid=wx123")

        // 在模拟器上 canOpenURL 返回 false,但 tryOpen 仍返回 true(已消费)
        XCTAssertTrue(result, "白名单命中应返回 true")
    }

    func test_alipays_in_whitelist_returns_true() {
        let handler = makeHandler(schemes: ["weixin", "alipays"])
        let result = handler.tryOpen("alipays://platformapi/startapp?appId=123")

        XCTAssertTrue(result)
    }

    // MARK: - 大小写不敏感

    func test_scheme_matching_is_case_insensitive() {
        let handler = makeHandler(schemes: ["weixin"])
        let result = handler.tryOpen("WEIXIN://wxa/test")

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

    func test_whitelist_uppercase_matches_lowercase_url() {
        let handler = makeHandler(schemes: ["WEIXIN"])
        let result = handler.tryOpen("weixin://wxa/test")

        XCTAssertTrue(result)
    }

    // MARK: - http(s) → 不处理

    func test_http_returns_false() {
        let handler = makeHandler()
        let result = handler.tryOpen("http://example.com")

        XCTAssertFalse(result, "http 不归 ExternalSchemeHandler 处理")
        XCTAssertTrue(events.isEmpty)
    }

    func test_https_returns_false() {
        let handler = makeHandler()
        let result = handler.tryOpen("https://example.com/page")

        XCTAssertFalse(result)
        XCTAssertTrue(events.isEmpty)
    }

    // MARK: - qianapp → 防御性兜底

    func test_qianapp_returns_false() {
        let handler = makeHandler(schemes: ["qianapp", "weixin"])
        let result = handler.tryOpen("qianapp://callback?action=sign")

        XCTAssertFalse(result, "qianapp 应由 SchemeInterceptor 处理,此处兜底返回 false")
        XCTAssertTrue(events.isEmpty)
    }

    // MARK: - nil / 空 → 返回 false

    func test_nil_url_returns_false() {
        let handler = makeHandler()
        let result = handler.tryOpen(nil)

        XCTAssertFalse(result)
        XCTAssertTrue(events.isEmpty)
    }

    func test_empty_url_returns_false() {
        let handler = makeHandler()
        let result = handler.tryOpen("")

        XCTAssertFalse(result)
        XCTAssertTrue(events.isEmpty)
    }

    // MARK: - 无效 URL → 返回 false

    func test_invalid_url_returns_false() {
        let handler = makeHandler()
        // 空格在 URL 中非法
        let result = handler.tryOpen("weixin://invalid url with spaces")

        // URL(string:) 对含空格的字符串可能返回 nil
        // 行为取决于 Foundation 版本,但不应崩溃
        _ = result // 不崩溃即通过
    }

    // MARK: - 空白名单

    func test_empty_whitelist_always_emits_schemeNotMatched() {
        let handler = makeHandler(schemes: [])
        let result = handler.tryOpen("weixin://wxa/test")

        XCTAssertTrue(result, "非 http/qianapp 的 scheme 都应被消费")
        XCTAssertEqual(events.count, 1)
        if case .schemeNotMatched = events.first {} else {
            XCTFail("空白名单应发出 schemeNotMatched")
        }
    }
}
