import XCTest

final class AttachmentUITests: ForgejoUITestBase {

    // Tests that multiple images can be uploaded from the markdown toolbar comment
    // sheet, and that both uploaded images appear in the issue's Attachments section
    // on reload. The -dev_testImageUpload launch argument bypasses the PhotosPicker
    // and uploads a hardcoded 1×1 PNG directly, keeping the test deterministic without
    // requiring photos in the simulator's library.
    @MainActor
    func testUploadMultipleImagesInCommentAndVerifyDisplayed() throws {
        app.launchArguments += ["-dev_testImageUpload"]
        loginAndWaitForHome()

        // Navigate to an existing issue
        navigateToIssueTab()
        let issueCell = app.staticTexts["Test issue 1 from integration tests"].firstMatch
        XCTAssertTrue(issueCell.waitForExistence(timeout: 10))
        issueCell.tap()
        XCTAssertTrue(app.staticTexts["issue-detail-title"].waitForExistence(timeout: 10))

        // Open the comment sheet
        expandActionMenu()
        let commentButton = app.buttons["issue-comment-button"]
        XCTAssertTrue(commentButton.waitForExistence(timeout: 5))
        commentButton.tap()

        // Tap the image upload button twice, in test mode each tap uploads a
        // hardcoded PNG instead of presenting the Photos picker. Two taps produce
        // two distinct attachments and two markdown references.
        let imageButton = app.buttons["markdown-toolbar-image"]
        XCTAssertTrue(imageButton.waitForExistence(timeout: 5))
        let textEditor = app.textViews["markdown-text-editor"].firstMatch
        XCTAssertTrue(textEditor.waitForExistence(timeout: 5))

        // First upload, wait until one markdown reference is inserted before tapping
        // again, so the second upload appends rather than racing the first.
        imageButton.tap()
        expectation(for: NSPredicate(format: "value MATCHES '.*!\\\\[.*'"), evaluatedWith: textEditor)
        waitForExpectations(timeout: 15, handler: nil)

        // Second upload, wait until two markdown references are present.
        imageButton.tap()
        let twoRefs = NSPredicate(format: "value MATCHES '(.*!\\\\[.*){2,}'")
        expectation(for: twoRefs, evaluatedWith: textEditor)
        waitForExpectations(timeout: 15, handler: nil)
        let editorValue = textEditor.value as? String ?? ""
        XCTAssertEqual(
            editorValue.components(separatedBy: "![").count - 1, 2,
            "Expected two markdown image references in the editor after two uploads"
        )

        // Submit the comment
        app.buttons["Submit"].tap()

        // Wait for the comment sheet (NavigationStack title "New Comment") to dismiss
        let commentSheetNavBar = app.navigationBars["New Comment"]
        expectation(for: NSPredicate(format: "exists == false"), evaluatedWith: commentSheetNavBar)
        waitForExpectations(timeout: 10, handler: nil)

        // Navigate back to the issue list
        app.navigationBars.buttons.firstMatch.tap()
        XCTAssertTrue(
            app.staticTexts["Test issue 1 from integration tests"].waitForExistence(timeout: 10),
            "Should return to the issue list after navigating back"
        )

        // Re-enter the issue: loadData() fetches the issue fresh,
        // now including the uploaded attachment in issue.assets.
        app.staticTexts["Test issue 1 from integration tests"].firstMatch.tap()
        XCTAssertTrue(app.staticTexts["issue-detail-title"].waitForExistence(timeout: 10))

        // Scroll past the issue header, milestone, assignees, and description
        // to reach the Attachments section that appears below them.
        app.swipeUp()
        app.swipeUp()

        // The Attachments section must now be visible with the gallery rendered.
        XCTAssertTrue(
            app.staticTexts["Attachments"].waitForExistence(timeout: 10),
            "Attachments section header should appear after uploading images to the issue"
        )
        let gallery = app.scrollViews["attachment-gallery"]
        XCTAssertTrue(
            gallery.waitForExistence(timeout: 5),
            "AttachmentGallery image scroll view should be rendered inside the Attachments section"
        )

        // Both uploaded images must render as thumbnails. Each thumbnail is a Link
        // wrapping an AsyncImage, exposed as an image element inside the gallery.
        let twoImages = NSPredicate(format: "count >= 2")
        expectation(for: twoImages, evaluatedWith: gallery.images)
        waitForExpectations(timeout: 10, handler: nil)
        XCTAssertGreaterThanOrEqual(
            gallery.images.count, 2,
            "Both uploaded images should render as thumbnails in the gallery"
        )
    }

    // Tests that a non-image attachment (a text/log file) is handled correctly:
    // the inserted markdown is a plain link (not an image embed), and after reload
    // the file renders as a file row (paperclip + name) rather than an image
    // thumbnail. The -dev_testImageUpload launch argument also exposes a companion
    // "Attach file" button that uploads a hardcoded server.log.
    @MainActor
    func testUploadLogFileInCommentRendersAsFileRow() throws {
        app.launchArguments += ["-dev_testImageUpload"]
        loginAndWaitForHome()

        // Navigate to an existing issue
        navigateToIssueTab()
        let issueCell = app.staticTexts["Test issue 2 from integration tests"].firstMatch
        XCTAssertTrue(issueCell.waitForExistence(timeout: 10))
        issueCell.tap()
        XCTAssertTrue(app.staticTexts["issue-detail-title"].waitForExistence(timeout: 10))

        // Open the comment sheet
        expandActionMenu()
        let commentButton = app.buttons["issue-comment-button"]
        XCTAssertTrue(commentButton.waitForExistence(timeout: 5))
        commentButton.tap()

        // Tap the file upload button, in test mode this uploads a hardcoded server.log.
        let fileButton = app.buttons["markdown-toolbar-file"]
        XCTAssertTrue(fileButton.waitForExistence(timeout: 5))
        let textEditor = app.textViews["markdown-text-editor"].firstMatch
        XCTAssertTrue(textEditor.waitForExistence(timeout: 5))
        fileButton.tap()

        // Wait for the upload to finish: a markdown reference is inserted.
        expectation(for: NSPredicate(format: "value CONTAINS 'server.log'"), evaluatedWith: textEditor)
        waitForExpectations(timeout: 15, handler: nil)

        // A non-image attachment must use link syntax [name](url), NOT image-embed
        // syntax ![name](url), otherwise it would render as a broken inline image.
        let editorValue = textEditor.value as? String ?? ""
        XCTAssertTrue(
            editorValue.contains("[server.log]"),
            "Expected a markdown link to the uploaded log file"
        )
        XCTAssertFalse(
            editorValue.contains("!["),
            "A non-image file must not use image-embed markdown syntax"
        )

        // Submit the comment
        app.buttons["Submit"].tap()
        let commentSheetNavBar = app.navigationBars["New Comment"]
        expectation(for: NSPredicate(format: "exists == false"), evaluatedWith: commentSheetNavBar)
        waitForExpectations(timeout: 10, handler: nil)

        // Navigate back to the issue list, then re-enter to reload issue.assets.
        app.navigationBars.buttons.firstMatch.tap()
        let listCell = app.staticTexts["Test issue 2 from integration tests"]
        XCTAssertTrue(listCell.waitForExistence(timeout: 10))
        listCell.firstMatch.tap()
        XCTAssertTrue(app.staticTexts["issue-detail-title"].waitForExistence(timeout: 10))

        // Scroll down to the Attachments section.
        app.swipeUp()
        app.swipeUp()

        XCTAssertTrue(
            app.staticTexts["Attachments"].waitForExistence(timeout: 10),
            "Attachments section header should appear after uploading a file"
        )
        // The log must render as a file row (not an image thumbnail), showing its name.
        // A SwiftUI Link may surface as a link or a button, so match any element type.
        let fileRow = app.descendants(matching: .any).matching(identifier: "attachment-file-row").firstMatch
        XCTAssertTrue(
            fileRow.waitForExistence(timeout: 5),
            "Non-image attachment should render as a file row"
        )
        XCTAssertTrue(
            app.staticTexts["server.log"].waitForExistence(timeout: 5),
            "File row should display the attachment file name"
        )
    }
}
