import XCTest

/// T-iPad-4 · Projects 大屏化的 UI 走查 —— 目的是产出可核对的 **截图**（iPad
/// 多列网格 vs iPhone 单列 List），非断言重逻辑（列数/detents 决策已由
/// `ProjectsLayoutTests` 单测覆盖）。
///
/// 与 harness 的契约同 `HappyPathUITests`：服务器 URL 经
/// `TEST_RUNNER_WEBTERM_SERVER_URL` 注入 runner。此测配对当前服务器 → 打开
/// Projects → 截图存为 `XCTAttachment(.keepAlways)`（宿主侧从 .xcresult 提取）。
///
/// iPad（regular）用横屏，让 `NavigationSplitView` 的 sidebar（含「项目」入口）
/// 常驻可见；iPhone（compact）保持竖屏走现有 stack。
@MainActor
final class ProjectsLayoutUITests: XCTestCase {
    private enum Deadline {
        static let launchSurface: TimeInterval = 30
        static let ui: TimeInterval = 20
        static let projects: TimeInterval = 25
        static let poll: TimeInterval = 0.5
    }

    override func setUpWithError() throws {
        continueAfterFailure = false
    }

    func testProjectsScreenScreenshot() throws {
        let server = try serverBaseURL()
        let isPad = UIDevice.current.userInterfaceIdiom == .pad
        if isPad {
            XCUIDevice.shared.orientation = .landscapeLeft
        }

        let app = XCUIApplication()
        app.launch()

        try revealSidebarIfNeeded(app, isPad: isPad)
        try pairCurrentServer(app, server: server)

        // 「项目」入口（leading 工具栏，两分支同 identifier）。
        let projectsButton = app.buttons["sessions.projectsButton"].firstMatch
        XCTAssertTrue(
            projectsButton.waitForExistence(timeout: Deadline.ui),
            "项目 toolbar button not found (on-screen: \(visibleStaticTexts(app)))"
        )
        // host 未激活时会 disabled —— 等到可点。
        let hittableDeadline = Date().addingTimeInterval(Deadline.ui)
        while !projectsButton.isHittable, Date() < hittableDeadline {
            Thread.sleep(forTimeInterval: Deadline.poll)
        }
        projectsButton.tap()

        // Projects sheet 出现：等导航标题「项目」或任一 seeded 项目名出现。
        let projectsTitle = app.navigationBars["项目"].firstMatch
        XCTAssertTrue(
            projectsTitle.waitForExistence(timeout: Deadline.projects),
            "Projects screen (项目) did not appear (on-screen: \(visibleStaticTexts(app)))"
        )
        // 给 /projects 拉取 + 网格布局一点时间落定。
        Thread.sleep(forTimeInterval: 2)

        let shot = XCTAttachment(screenshot: app.screenshot())
        shot.name = isPad ? "ipad_projects_grid" : "iphone_projects_list"
        shot.lifetime = .keepAlways
        add(shot)
    }

    // MARK: - Sidebar reveal (iPad split, collapsed in某些尺寸)

    private func revealSidebarIfNeeded(_ app: XCUIApplication, isPad: Bool) throws {
        guard isPad else { return }
        let projectsButton = app.buttons["sessions.projectsButton"].firstMatch
        if projectsButton.waitForExistence(timeout: 3) { return }
        // 尝试系统 sidebar 切换钮；失败则从左边缘轻扫。
        let toggle = app.navigationBars.buttons.matching(
            NSPredicate(format: "identifier CONTAINS[c] %@", "sidebar")
        ).firstMatch
        if toggle.exists {
            toggle.tap()
        } else {
            let edge = app.coordinate(withNormalizedOffset: CGVector(dx: 0.0, dy: 0.4))
            let inward = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.4))
            edge.press(forDuration: 0.1, thenDragTo: inward)
        }
        _ = projectsButton.waitForExistence(timeout: Deadline.ui)
    }

    // MARK: - Pairing (tolerant, mirrors HappyPathUITests)

    private func serverBaseURL() throws -> URL {
        let raw = ProcessInfo.processInfo.environment["WEBTERM_SERVER_URL"]
        guard let raw, let url = URL(string: raw), url.host != nil else {
            XCTFail("WEBTERM_SERVER_URL missing/invalid in runner env — pass TEST_RUNNER_WEBTERM_SERVER_URL")
            throw NSError(domain: "ProjectsLayoutUITests", code: 1)
        }
        return url
    }

    private func pairCurrentServer(_ app: XCUIApplication, server: URL) throws {
        let urlField = app.textFields["pairing.urlField"].firstMatch
        let hostMenu = app.buttons["sessions.hostMenu"].firstMatch
        let emptyAddHost = app.buttons["配对新主机"].firstMatch

        let deadline = Date().addingTimeInterval(Deadline.launchSurface)
        while Date() < deadline {
            if urlField.exists {
                try fillPairingForm(app, server: server)
                return
            }
            if hostMenu.exists || emptyAddHost.exists {
                if hostMenu.exists { hostMenu.tap() }
                XCTAssertTrue(
                    emptyAddHost.waitForExistence(timeout: Deadline.ui),
                    "配对新主机 entry not found"
                )
                emptyAddHost.tap()
                try fillPairingForm(app, server: server)
                try selectJustPairedHost(app, server: server, hostMenu: hostMenu)
                return
            }
            Thread.sleep(forTimeInterval: Deadline.poll)
        }
        XCTFail("neither PairingScreen nor session list appeared (on-screen: \(visibleStaticTexts(app)))")
    }

    private func fillPairingForm(_ app: XCUIApplication, server: URL) throws {
        let urlField = app.textFields["pairing.urlField"].firstMatch
        XCTAssertTrue(urlField.waitForExistence(timeout: Deadline.ui), "pairing URL field not found")
        urlField.tap()
        urlField.typeText(server.absoluteString)
        app.buttons["pairing.submitButton"].firstMatch.tap()

        let confirmButton = app.buttons["pairing.confirmButton"].firstMatch
        XCTAssertTrue(
            confirmButton.waitForExistence(timeout: Deadline.ui),
            "pairing confirm page not reached (on-screen: \(visibleStaticTexts(app)))"
        )
        confirmButton.tap()

        let pairingNavBar = app.navigationBars["配对主机"].firstMatch
        let retryButton = app.buttons["重试"].firstMatch
        let resolveDeadline = Date().addingTimeInterval(Deadline.ui)
        while pairingNavBar.exists {
            if retryButton.exists {
                XCTFail("pairing probe FAILED (on-screen: \(visibleStaticTexts(app)))")
                return
            }
            if Date() >= resolveDeadline {
                XCTFail("pairing did not complete (on-screen: \(visibleStaticTexts(app)))")
                return
            }
            Thread.sleep(forTimeInterval: Deadline.poll)
        }
    }

    private func selectJustPairedHost(
        _ app: XCUIApplication, server: URL, hostMenu: XCUIElement
    ) throws {
        let hostLabel = server.host ?? "127.0.0.1"
        XCTAssertTrue(hostMenu.waitForExistence(timeout: Deadline.ui), "host menu not visible")
        let hittableDeadline = Date().addingTimeInterval(Deadline.ui)
        while !hostMenu.isHittable, Date() < hittableDeadline {
            Thread.sleep(forTimeInterval: Deadline.poll)
        }
        hostMenu.tap()
        let rows = app.buttons.matching(
            NSPredicate(format: "label == %@ AND identifier != %@", hostLabel, "sessions.hostMenu")
        )
        let rowDeadline = Date().addingTimeInterval(Deadline.ui)
        while rows.count == 0, Date() < rowDeadline {
            Thread.sleep(forTimeInterval: Deadline.poll)
        }
        guard let lastRow = rows.allElementsBoundByIndex.last else {
            XCTFail("no host menu row labelled \(hostLabel)")
            return
        }
        lastRow.tap()
    }

    private func visibleStaticTexts(_ app: XCUIApplication) -> String {
        app.staticTexts.allElementsBoundByIndex.prefix(12).map(\.label).joined(separator: " | ")
    }
}
