import XCTest

/// Board list → lanes → cards → card detail, then an edit that has to reach disk — the one
/// end-to-end walk of the mobile MVP's navigation stack (`BoardRoute`'s three destinations), plus
/// the transactional edit sheet `CardDetailScreen`'s Edit button presents.
final class BoardsNavigationUITests: XCTestCase {

    @MainActor
    func testBoardsNavigationAndTitleEdit() throws {
        let (app, root) = XCUIApplication.launchedWithFixtureBoard()
        app.navigateToFirstCard()

        // CardDetailScreen is read-only: the fixture card's title renders as plain text, not a
        // field — the read/edit split this suite now has to cross to reach any field at all.
        XCTAssertTrue(
            app.staticTexts[RichBoard.firstLaneFirstCard].waitForExistence(timeout: XCUIApplication.uiTimeout),
            "the read view's title text never appeared"
        )

        app.openCardEdit()

        // CardEditScreen opens on the Details pane; its only textField is the title
        // (`Section("Title")`) — the body pane is a bare `TextEditor`, a `textView`, and is not
        // this pane's concern.
        let titleField = app.textFields.firstMatch
        XCTAssertTrue(
            titleField.waitForExistence(timeout: XCUIApplication.uiTimeout),
            "CardEditScreen's title field never appeared"
        )
        XCTAssertEqual(
            titleField.value as? String, RichBoard.firstLaneFirstCard,
            "the title field did not seed with the fixture card's own title"
        )

        let sentinel = "Retitled by UI test"
        titleField.replaceAllText(with: sentinel)

        app.navigationBars.buttons["Save"].tap()

        // The cover dismisses back to the read view underneath, which should already show the new
        // title — `session.perform` awaits its own reload before `Save` dismisses.
        XCTAssertTrue(
            app.staticTexts[sentinel].waitForExistence(timeout: XCUIApplication.uiTimeout),
            "the read view never picked up the retitled card"
        )
        XCTAssertTrue(
            waitForFile(under: root, containing: sentinel),
            "the retitled card never landed on disk under \(root.path)"
        )
    }

    @MainActor
    func testCardEditBodyPaneWritesToDisk() throws {
        let (app, root) = XCUIApplication.launchedWithFixtureBoard()
        app.navigateToFirstCard()
        app.openCardEdit()

        app.buttons["Body"].tap()

        let bodyEditor = app.textViews.firstMatch
        XCTAssertTrue(
            bodyEditor.waitForExistence(timeout: XCUIApplication.uiTimeout),
            "the Body pane's text editor never appeared"
        )

        let sentinel = "Rewritten body by UI test"
        bodyEditor.replaceAllText(with: sentinel)

        app.navigationBars.buttons["Save"].tap()

        // The read view renders the body as Markdown; a plain sentinel with no markup renders
        // back out as itself.
        XCTAssertTrue(
            app.staticTexts[sentinel].waitForExistence(timeout: XCUIApplication.uiTimeout),
            "the read view never picked up the rewritten body"
        )
        XCTAssertTrue(
            waitForFile(under: root, containing: sentinel),
            "the rewritten body never landed on disk under \(root.path)"
        )
    }

    @MainActor
    func testCardEditCancelWithDirtyDraftsDiscardsOnDisk() throws {
        let (app, root) = XCUIApplication.launchedWithFixtureBoard()
        app.navigateToFirstCard()
        app.openCardEdit()

        let titleField = app.textFields.firstMatch
        XCTAssertTrue(
            titleField.waitForExistence(timeout: XCUIApplication.uiTimeout),
            "CardEditScreen's title field never appeared"
        )

        let sentinel = "Abandoned edit"
        titleField.replaceAllText(with: sentinel)

        // Cancel on a dirty sheet raises a confirmation rather than dismissing outright — the
        // sheet's only exit, since a `fullScreenCover` has no interactive swipe-dismiss.
        app.navigationBars.buttons["Cancel"].tap()

        let discardButton = app.buttons["Discard"]
        XCTAssertTrue(
            discardButton.waitForExistence(timeout: XCUIApplication.uiTimeout),
            "the discard confirmation never appeared for a dirty Cancel"
        )
        discardButton.tap()

        // Back on the read view, still showing the fixture's own title — nothing was ever written.
        XCTAssertTrue(
            app.staticTexts[RichBoard.firstLaneFirstCard].waitForExistence(timeout: XCUIApplication.uiTimeout),
            "the read view did not return to the card's original title after Discard"
        )
        XCTAssertTrue(
            fileDoesNotContain(under: root, substring: sentinel),
            "the discarded title leaked onto disk under \(root.path)"
        )
    }
}

private extension XCUIApplication {

    // `navigateToFirstCard()` moved to `MobileUITestSupport.swift` — the comments suite starts on
    // the same card.

    /// Taps `CardDetailScreen`'s Edit button and waits for `CardEditScreen`'s cover to land.
    @MainActor
    func openCardEdit() {
        let editButton = navigationBars.buttons["Edit"]
        XCTAssertTrue(
            editButton.waitForExistence(timeout: XCUIApplication.uiTimeout),
            "the read view's Edit button never appeared"
        )
        editButton.tap()

        XCTAssertTrue(
            navigationBars.buttons["Save"].waitForExistence(timeout: XCUIApplication.uiTimeout),
            "CardEditScreen's cover never appeared"
        )
    }
}
