//
//  GlobalLoadingCenterTests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/23.
//

import XCTest
@testable import suixinkan

@MainActor
/// 全局 Loading 命令中心测试，验证展示状态不会泄漏给业务页面使用。
final class GlobalLoadingCenterTests: XCTestCase {
    /// 测试多次 show/hide 会按引用计数控制最终显隐。
    func testShowHideUsesReferenceCount() {
        let center = GlobalLoadingCenter()

        center.show(message: "加载账号")
        center.show(message: "加载权限")

        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: true, message: "加载权限", activeCount: 2))

        center.hide()
        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: true, message: "加载权限", activeCount: 1))

        center.hide()
        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: false, message: "", activeCount: 0))
    }

    /// 测试 hide 调用次数超过 show 时不会产生负数计数。
    func testHideNeverDropsReferenceCountBelowZero() {
        let center = GlobalLoadingCenter()

        center.hide()

        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: false, message: "", activeCount: 0))
    }

    /// 测试更新文案只影响当前展示内容，不改变引用计数。
    func testUpdateMessageOnlyChangesVisibleMessage() {
        let center = GlobalLoadingCenter()

        center.updateMessage("不会展示")
        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: false, message: "", activeCount: 0))

        center.show(message: "加载中")
        center.updateMessage("即将完成")

        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: true, message: "即将完成", activeCount: 1))
    }

    /// 测试 withLoading 成功完成后自动隐藏 Loading。
    func testWithLoadingHidesAfterSuccess() async throws {
        let center = GlobalLoadingCenter()

        let value = try await center.withLoading(message: "提交中") {
            XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: true, message: "提交中", activeCount: 1))
            return 42
        }

        XCTAssertEqual(value, 42)
        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: false, message: "", activeCount: 0))
    }

    /// 测试 withLoading 抛错时也会自动隐藏 Loading。
    func testWithLoadingHidesAfterFailure() async {
        let center = GlobalLoadingCenter()

        do {
            _ = try await center.withLoading(message: "提交中") {
                throw TestFailure.expected
            } as Int
            XCTFail("withLoading 应该向外抛出业务错误")
        } catch {
            XCTAssertEqual(error as? TestFailure, .expected)
        }

        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: false, message: "", activeCount: 0))
    }

    /// 测试 withOptionalLoading 关闭时不会改变展示状态。
    func testWithOptionalLoadingDisabledDoesNotChangeState() async throws {
        let center = GlobalLoadingCenter()

        let value = try await center.withOptionalLoading(false, message: "不展示") {
            XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: false, message: "", activeCount: 0))
            return "done"
        }

        XCTAssertEqual(value, "done")
        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: false, message: "", activeCount: 0))
    }

    /// 测试使用 Loading 不需要读取可观察展示状态，业务侧只能拿到命令中心类型。
    func testBusinessCodeOnlyDependsOnCommandCenter() {
        let center = GlobalLoadingCenter()

        issueLoadingCommand(center)

        XCTAssertEqual(center.snapshotForTests, GlobalLoadingSnapshot(isVisible: true, message: "加载中", activeCount: 1))
        center.hide()
    }

    /// 模拟业务代码只接收命令中心并发出展示指令。
    private func issueLoadingCommand(_ center: GlobalLoadingCenter) {
        center.show(message: "加载中")
    }

    /// 测试用错误类型。
    private enum TestFailure: Error, Equatable {
        case expected
    }
}
