import XCTest

final class RepositoryUITests: ForgejoReadOnlyUITestBase {

    // MARK: - Repository List (repos, star, filter, search)

    @MainActor
    func testRepositoryList() throws {
        app.tabBars.buttons["Repositories"].tap()

        let repoList = app.collectionViews["repo-list"]
        XCTAssertTrue(repoList.waitForExistence(timeout: 10))

        // Both repos visible
        XCTAssertTrue(app.staticTexts["test-repo"].waitForExistence(timeout: 5))
        XCTAssertTrue(app.staticTexts["test-repo-2"].waitForExistence(timeout: 5))

        // Filter by Starred
        let starredButton = app.buttons["Starred"]
        XCTAssertTrue(starredButton.waitForExistence(timeout: 5))
        starredButton.tap()

        // Switch back to All
        let allButton = app.buttons["All"]
        XCTAssertTrue(allButton.waitForExistence(timeout: 5))
        allButton.tap()
        XCTAssertTrue(app.staticTexts["test-repo"].waitForExistence(timeout: 10))

        // Search
        let searchField = app.searchFields.firstMatch
        XCTAssertTrue(searchField.waitForExistence(timeout: 5))
        searchField.tap()
        searchField.typeText("test-repo-2")

        XCTAssertTrue(app.staticTexts["test-repo-2"].waitForExistence(timeout: 10))

        // Clear search to restore full list
        searchField.buttons["Clear text"].tap()
        XCTAssertTrue(app.staticTexts["test-repo"].waitForExistence(timeout: 10))

        // Star toggle (last, tap triggers NavigationLink, only tests tappability)
        let starButton = repoList.buttons["star-button"].firstMatch
        XCTAssertTrue(starButton.waitForExistence(timeout: 5), "No star button found in repo list")
        starButton.tap()
    }

    // MARK: - Archived Badge

    @MainActor
    func testArchivedBadgeInRepoList() throws {
        app.tabBars.buttons["Repositories"].tap()

        let repoList = app.collectionViews["repo-list"]
        XCTAssertTrue(repoList.waitForExistence(timeout: 10))

        // Archived repo should show the "Archived" badge
        XCTAssertTrue(app.staticTexts["archived-repo"].waitForExistence(timeout: 5), "archived-repo should be visible in repo list")
        XCTAssertTrue(app.staticTexts["Archived"].exists, "Archived badge should be visible for archived-repo")

        // Only one Archived badge should exist in the list
        let archivedBadges = app.staticTexts.matching(identifier: "Archived")
        XCTAssertEqual(archivedBadges.count, 1, "Only one Archived badge should be visible (for archived-repo)")
    }

    @MainActor
    func testArchivedBadgeInRepoDetail() throws {
        navigateToRepoDetail("archived-repo")

        // The detail view header should show the "Archived" badge
        XCTAssertTrue(app.staticTexts["Archived"].waitForExistence(timeout: 10), "Archived badge should be visible in repo detail")
    }

    @MainActor
    func testNoArchivedBadgeForNonArchivedRepo() throws {
        navigateToRepoDetail("test-repo")

        // Wait for the detail view to load
        XCTAssertTrue(app.staticTexts["test-repo"].waitForExistence(timeout: 10))

        // The "Archived" badge should not appear
        XCTAssertFalse(app.staticTexts["Archived"].exists, "Archived badge should not be visible for non-archived repo")
    }

    // MARK: - Repository Filter Menu

    @MainActor
    func testFilterHidesArchivedRepos() throws {
        app.tabBars.buttons["Repositories"].tap()

        let repoList = app.collectionViews["repo-list"]
        XCTAssertTrue(repoList.waitForExistence(timeout: 10))
        XCTAssertTrue(app.staticTexts["archived-repo"].waitForExistence(timeout: 5))

        // Open filter menu and disable archived repos
        let filterButton = app.buttons["filter-menu-button"]
        XCTAssertTrue(filterButton.waitForExistence(timeout: 5))
        filterButton.tap()

        let archivedToggle = app.buttons["Archived"]
        XCTAssertTrue(archivedToggle.waitForExistence(timeout: 5))
        archivedToggle.tap()

        // Archived repo should be hidden, non-archived repos still visible
        XCTAssertTrue(app.staticTexts["test-repo"].waitForExistence(timeout: 5))
        XCTAssertFalse(app.staticTexts["archived-repo"].waitForExistence(timeout: 2),
                       "archived-repo should be hidden when archived filter is off")

        // Re-enable archived repos
        filterButton.tap()
        let archivedToggleAgain = app.buttons["Archived"]
        XCTAssertTrue(archivedToggleAgain.waitForExistence(timeout: 5))
        archivedToggleAgain.tap()

        // Archived repo should be visible again
        XCTAssertTrue(app.staticTexts["archived-repo"].waitForExistence(timeout: 5),
                       "archived-repo should reappear when archived filter is on")
    }

    override func tearDown() {
        navigateBackToHome()
        super.tearDown()
    }

    // MARK: - HTML in Markdown Rendering

    @MainActor
    func testHTMLInMarkdownRendering() throws {
        navigateToRepoDetail("html-readme-repo")

        // Wait for repo detail to load
        XCTAssertTrue(app.staticTexts["html-readme-repo"].waitForExistence(timeout: 10))

        // Raw HTML tags should NOT appear as plain text, they should be rendered
        XCTAssertFalse(
            app.staticTexts["<details>"].waitForExistence(timeout: 5),
            "Raw <details> tag should not be visible as plain text")
        XCTAssertFalse(
            app.staticTexts["<table>"].waitForExistence(timeout: 2),
            "Raw <table> tag should not be visible as plain text")
        XCTAssertFalse(
            app.staticTexts["<kbd>"].waitForExistence(timeout: 2),
            "Raw <kbd> tag should not be visible as plain text")
    }

    // MARK: - HTML Sanitization

    @MainActor
    func testDangerousHTMLIsStripped() throws {
        navigateToRepoDetail("html-readme-repo")

        XCTAssertTrue(app.staticTexts["html-readme-repo"].waitForExistence(timeout: 10))

        // Script tags must be stripped by DOMPurify
        XCTAssertFalse(
            app.staticTexts["alert(\"xss\")"].waitForExistence(timeout: 5),
            "Script content should be stripped by DOMPurify")

        // Iframes must be stripped
        XCTAssertFalse(
            app.staticTexts["<iframe"].waitForExistence(timeout: 2),
            "Iframe tags should be stripped by DOMPurify")

        // onclick attributes must be stripped (the div text may render, but not as a clickable handler)
        // Verify the safe content still renders
        XCTAssertFalse(
            app.staticTexts["onclick"].waitForExistence(timeout: 2),
            "onclick attributes should be stripped by DOMPurify")
    }

    // MARK: - Branch Selector

    @MainActor
    func testBranchSelector() throws {
        navigateToRepoDetail()

        // Branch selector should exist and show "main" by default
        let branchSelector = app.buttons["branch-selector"]
        XCTAssertTrue(branchSelector.waitForExistence(timeout: 10), "Branch selector not found")
        XCTAssertTrue(branchSelector.label.contains("main"), "Branch selector should show 'main' initially")

        // Verify main branch files are visible
        XCTAssertTrue(app.staticTexts["hello.py"].waitForExistence(timeout: 10))

        // Switch to feature-branch via branch picker sheet
        branchSelector.tap()
        let featureBranchOption = app.buttons["branch-option-feature-branch"]
        XCTAssertTrue(featureBranchOption.waitForExistence(timeout: 5), "feature-branch option not found")
        featureBranchOption.tap()

        // feature.txt should appear (only exists on feature-branch)
        XCTAssertTrue(app.staticTexts["feature.txt"].waitForExistence(timeout: 10), "feature.txt should be visible on feature-branch")

        // Switch back to main
        let updatedSelector = app.buttons["branch-selector"]
        XCTAssertTrue(updatedSelector.waitForExistence(timeout: 5))
        updatedSelector.tap()
        let mainBranchOption = app.buttons["branch-option-main"]
        XCTAssertTrue(mainBranchOption.waitForExistence(timeout: 5), "main option not found")
        mainBranchOption.tap()

        // feature.txt should no longer be visible, hello.py should be back
        XCTAssertTrue(app.staticTexts["hello.py"].waitForExistence(timeout: 10), "hello.py should be visible on main")
    }
}
