//
//  HomeMenuRouterTests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/22.
//

import XCTest
@testable import suixinkan

/// 首页菜单路由测试，覆盖 URI 映射、别名和未知路由诊断。
final class HomeMenuRouterTests: XCTestCase {
    override func setUp() {
        super.setUp()
        HomeRouteDiagnostics.resetUnknownRoutes()
    }

    override func tearDown() {
        HomeRouteDiagnostics.resetUnknownRoutes()
        super.tearDown()
    }

    /// 测试已迁移的首页路由会解析到真实目标。
    func testMigratedRoutesResolveToDestinations() {
        let expectations: [(String, HomeMenuResolvedRoute)] = [
            ("space_settings", .destination(.profileSpaceSettings)),
            ("scenicselection", .destination(.scenicSelection)),
            ("store", .destination(.moreFunctions)),
            ("wallet", .destination(.wallet)),
            ("payment_collection", .destination(.paymentCollection)),
            ("payment_qr", .destination(.paymentCollection)),
            ("payment_code", .destination(.paymentCollection)),
            ("permission_apply", .destination(.permissionApply)),
            ("permission_apply_status", .destination(.permissionApplyStatus)),
            ("scenicapplication", .destination(.scenicApplication)),
            ("task_management", .destination(.taskManagement)),
            ("task_management_editor", .destination(.taskManagement)),
            ("task_create", .destination(.taskCreate)),
            ("cloud_management", .destination(.cloudStorage)),
            ("cloud_storage_transit", .destination(.cloudStorageTransit)),
            ("asset_management", .destination(.materialLibrary)),
            ("material_upload", .destination(.materialUpload)),
            ("album_list", .destination(.albumList)),
            ("album_trailer", .destination(.albumTrailer)),
            ("sample_management", .destination(.sampleLibrary)),
            ("sample_upload", .destination(.sampleUpload)),
            ("checkin_points", .destination(.punchPointList)),
            ("location_report", .destination(.locationReport)),
            ("location_report_history", .destination(.locationReportHistory)),
            ("report_photographer", .destination(.photographerReport)),
            ("pm", .destination(.projectManagement)),
            ("project_edit", .destination(.projectManagement)),
            ("pm_manager", .destination(.pmProjectManagement)),
            ("schedule_management", .destination(.scheduleManagement)),
            ("registration_invitation", .destination(.photographerInvite)),
            ("photographer_invite", .destination(.photographerInvite)),
            ("invite_record", .destination(.inviteRecord)),
            ("deposit_order_detail", .destination(.depositOrders)),
            ("deposit_order", .destination(.depositOrders)),
            ("deposit_order_shooting_info", .destination(.depositOrders)),
            ("withdrawal_audit", .destination(.withdrawalAudit)),
            ("cooperation_order", .destination(.cooperationOrder)),
            ("scenic_settlement", .destination(.scenicSettlement)),
            ("scenic_settlement_review", .destination(.scenicSettlementReview)),
            ("message_center", .destination(.messageCenter)),
            ("/scenic-queue", .destination(.queueManagement)),
            ("queue_management", .destination(.queueManagement)),
            ("live_stream_management", .destination(.liveManagement)),
            ("live_album", .destination(.liveAlbum)),
            ("travel_album", .destination(.travelAlbumEntry)),
            ("operating-area", .destination(.operatingArea)),
            ("pilot_cert", .destination(.pilotCertification))
        ]

        for (uri, expected) in expectations {
            XCTAssertEqual(HomeMenuRouter.resolve(uri: uri, title: ""), expected, "URI \(uri) should resolve to migrated destination")
        }
    }

    /// 测试 Tab 路由仍集中在 HomeMenuRouter。
    func testTabRoutesStayCentralized() {
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_orders", title: ""), .orders(.storeOrders))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "/scenic-order-manage", title: ""), .orders(.storeOrders))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "verification_order", title: ""), .orderPush(.writeOffList))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_stats", title: ""), .tab(.statistics))
    }

    /// 测试飞控相关入口属于已知不支持范围。
    func testFlightControlRoutesAreKnownUnsupportedScope() {
        XCTAssertEqual(
            HomeMenuRouter.resolve(uri: "fly", title: ""),
            .unsupported(uri: "fly", title: "飞行管理", reason: "DJI/飞控模块已明确不纳入 iOS 迁移和后续开发范围。")
        )
    }

    /// 测试未知 URI 会显式进入 placeholder。
    func testUnknownRouteIsExplicitPlaceholder() {
        XCTAssertEqual(
            HomeMenuRouter.resolve(uri: "android_only_feature", title: "Android 专项"),
            .placeholder(uri: "android_only_feature", title: "Android 专项")
        )
    }

    /// 测试 canonicalURI 会使用当前权限可用的同义 URI。
    func testCanonicalURIUsesAvailableAlias() {
        let available: Set<String> = ["task_management_editor", "photographer_invite", "project_edit", "pm_manager", "payment_qr"]
        XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "task_management", availableURIs: available), "task_management_editor")
        XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "registration_invitation", availableURIs: available), "photographer_invite")
        XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "pm", availableURIs: available), "project_edit")
        XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "pm_manager", availableURIs: available), "pm_manager")
        XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "payment_code", availableURIs: available), "payment_qr")
    }

    /// 测试 menuAliasKey 会把同义入口分到同一组。
    func testMenuAliasKeyGroupsDuplicateAndroidEntries() {
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "task_management_editor"), "task_management")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "photographer_invite"), "registration_invitation")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "project_edit"), "pm")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "pm_manager"), "pm_manager")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "payment_code"), "payment_collection")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "deposit_order_shooting_info"), "deposit_order")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "/scenic-order-manage"), "photographer_orders")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "/scenic-queue"), "queue_management")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "queue_management"), "queue_management")
        XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "android_only_module"), "android_only_module")
    }

    /// 测试未知路由诊断会去重并更新标题。
    func testUnknownRouteDiagnosticsDeduplicatesAndUpdatesTitle() {
        HomeRouteDiagnostics.recordUnknown(uri: "android_feature", title: "旧标题")
        HomeRouteDiagnostics.recordUnknown(uri: "android_feature", title: "新标题")
        HomeRouteDiagnostics.recordUnknown(uri: "another_feature", title: "")

        let records = HomeRouteDiagnostics.unknownRoutes()

        XCTAssertEqual(records.count, 2)
        XCTAssertEqual(records.first { $0.uri == "android_feature" }?.title, "新标题")
        XCTAssertEqual(records.first { $0.uri == "android_feature" }?.count, 2)
        XCTAssertEqual(records.first { $0.uri == "another_feature" }?.count, 1)
    }

    /// 测试未知路由诊断报告是 Markdown 且按出现次数排序。
    func testUnknownRouteDiagnosticsReportIsMarkdownAndSortedByCount() {
        HomeRouteDiagnostics.recordUnknown(uri: "rare_feature", title: "低频入口")
        HomeRouteDiagnostics.recordUnknown(uri: "hot_feature", title: "高频入口")
        HomeRouteDiagnostics.recordUnknown(uri: "hot_feature", title: "高频入口")

        let report = HomeRouteDiagnostics.unknownRouteReport(generatedAt: Date(timeIntervalSince1970: 0))

        XCTAssertTrue(report.contains("# Home Route Diagnostics"))
        XCTAssertTrue(report.contains("| hot_feature | 高频入口 | 2 |"))
        XCTAssertLessThan(
            report.range(of: "hot_feature")!.lowerBound,
            report.range(of: "rare_feature")!.lowerBound
        )
    }

    /// 测试权限路由审计会区分可路由、不支持和未知入口。
    func testPermissionRouteAuditSeparatesRoutableUnsupportedAndUnknownEntries() {
        let permissions = [
            rolePermission(
                roleCode: "photographer",
                roleId: 1,
                permissions: [
                    permission(id: 1, name: "任务管理", uri: "task_management", children: [
                        permission(id: 2, name: "发布任务", uri: "task_create")
                    ]),
                    permission(id: 3, name: "飞控", uri: "pilot_controller"),
                    permission(id: 4, name: "Android 专项入口", uri: "android_only_module"),
                    permission(id: 5, name: "重复任务", uri: "task_management")
                ]
            )
        ]

        let audit = HomePermissionRouteAuditor.audit(permissions: permissions, currentRoleCode: "photographer")

        XCTAssertEqual(audit.routable.map(\.uri), ["task_management", "task_create"])
        XCTAssertEqual(audit.unsupported.map(\.uri), ["pilot_controller"])
        XCTAssertEqual(audit.unknown.map(\.uri), ["android_only_module"])
        XCTAssertTrue(audit.hasUnknownRoutes)
    }

    #if DEBUG
    /// 测试 DEBUG 菜单列表不依赖权限，包含已迁移和未迁移入口。
    func testDebugAllMenuItemsExposeKnownMenusWithoutPermissions() {
        let items = HomeMenuRouter.debugAllMenuItems()
        let uris = items.map(\.uri)

        XCTAssertTrue(uris.contains("album_list"))
        XCTAssertTrue(uris.contains("wallet"))
        XCTAssertTrue(uris.contains("sample_management"))
        XCTAssertTrue(uris.contains("checkin_points"))
        XCTAssertTrue(uris.contains("location_report"))
        XCTAssertTrue(uris.contains("report_photographer"))
        XCTAssertTrue(uris.contains("pm"))
        XCTAssertTrue(uris.contains("pm_manager"))
        XCTAssertTrue(uris.contains("schedule_management"))
        XCTAssertTrue(uris.contains("registration_invitation"))
        XCTAssertTrue(uris.contains("invite_record"))
        XCTAssertTrue(uris.contains("deposit_order"))
        XCTAssertTrue(uris.contains("deposit_order_shooting_info"))
        XCTAssertTrue(uris.contains("message_center"))
        XCTAssertTrue(uris.contains("/scenic-queue"))
        XCTAssertTrue(uris.contains("queue_management"))
        XCTAssertTrue(uris.contains("live_stream_management"))
        XCTAssertTrue(uris.contains("live_album"))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_list", title: ""), .destination(.albumList))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_management", title: ""), .destination(.sampleLibrary))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_upload", title: ""), .destination(.sampleUpload))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "checkin_points", title: ""), .destination(.punchPointList))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "location_report", title: ""), .destination(.locationReport))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "report_photographer", title: ""), .destination(.photographerReport))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm", title: ""), .destination(.projectManagement))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm_manager", title: ""), .destination(.pmProjectManagement))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "schedule_management", title: ""), .destination(.scheduleManagement))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "registration_invitation", title: ""), .destination(.photographerInvite))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "invite_record", title: ""), .destination(.inviteRecord))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order", title: ""), .destination(.depositOrders))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order_shooting_info", title: ""), .destination(.depositOrders))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "message_center", title: ""), .destination(.messageCenter))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "/scenic-queue", title: ""), .destination(.queueManagement))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "queue_management", title: ""), .destination(.queueManagement))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "live_stream_management", title: ""), .destination(.liveManagement))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "live_album", title: ""), .destination(.liveAlbum))
        XCTAssertEqual(HomeMenuRouter.resolve(uri: "travel_album", title: ""), .destination(.travelAlbumEntry))
    }
    #endif

    /// 创建角色权限测试实体。
    private func rolePermission(roleCode: String, roleId: Int, permissions: [PermissionItem]) -> RolePermissionResponse {
        RolePermissionResponse(
            role: RoleInfo(id: roleId, name: "角色\(roleId)", roleCode: roleCode, permission: permissions),
            scenic: []
        )
    }

    /// 创建权限节点测试实体。
    private func permission(id: Int, name: String, uri: String, children: [PermissionItem] = []) -> PermissionItem {
        PermissionItem(id: id, name: name, uri: uri, children: children)
    }
}
