import XCTest

/// Cloud browser smoke tests — verifies cloud music browsing flows.
/// Requires Chad Music server to be running for Phase 1 tests.
/// Phase 3 tests use `-MockNetwork` for CI-reliable offline testing.
final class CloudBrowserUITests: XCTestCase {

    var app: XCUIApplication!

    override func setUpWithError() throws {
        continueAfterFailure = false
        app = XCUIApplication()
        app.launchArguments += ["-UITesting"]
    }

    override func tearDownWithError() throws {
        app = nil
    }

    // MARK: - Helpers

    /// Opens the cloud browser sheet by tapping the cloud button in the toolbar.
    private func openCloudBrowser() {
        let cloudButton = app.buttons["cloudBrowserButton"]
        XCTAssertTrue(cloudButton.waitForExistence(timeout: 5), "Cloud browser button should exist")
        cloudButton.tap()
    }

    // MARK: - Phase 1: Cloud Browser Smoke Tests (live server)

    /// Verifies the cloud browser opens and shows the Browse section with all category types.
    func testCloudHomeLoads() {
        app.launch()
        openCloudBrowser()

        // Browse section should appear
        let albumsLink = app.buttons["cloud.browse.album"]

        // Wait for a category link to appear
        let browseAppeared = albumsLink.waitForExistence(timeout: 5)

        // If server is configured, categories should appear
        if browseAppeared {
            // Verify key category types are listed
            let expectedCategories = ["album", "artist", "genre", "year"]
            for cat in expectedCategories {
                let link = app.buttons["cloud.browse.\(cat)"]
                XCTAssertTrue(link.exists, "\(cat) category should be listed in Browse section")
            }
        }
        // If not configured, the "Not Connected" view appears — that's OK for CI
    }

    /// Verifies each category type can be tapped and loads without showing error text.
    func testAllCategoriesLoad() {
        app.launch()
        openCloudBrowser()

        let albumsLink = app.buttons["cloud.browse.album"]
        guard albumsLink.waitForExistence(timeout: 5) else {
            // Server not configured, skip gracefully
            return
        }

        // Test non-album categories (album has its own view)
        let categories = ["artist", "genre", "year", "publisher"]
        for cat in categories {
            let link = app.buttons["cloud.browse.\(cat)"]
            guard link.waitForExistence(timeout: 3) else { continue }
            link.tap()

            // Wait for loading to complete
            let errorText = app.staticTexts.matching(identifier: "cloud.category.error").firstMatch
            let list = app.otherElements["cloud.category.list"]

            // Give it time to load
            _ = list.waitForExistence(timeout: 10)

            // Error text should NOT appear
            XCTAssertFalse(errorText.exists, "\(cat) category should load without error")

            // Navigate back
            app.navigationBars.buttons.firstMatch.tap()
        }
    }

    /// Verifies navigating to Albums → tapping an album → tracks appear.
    func testAlbumDetailOpens() {
        app.launch()
        openCloudBrowser()

        let albumsLink = app.buttons["cloud.browse.album"]
        guard albumsLink.waitForExistence(timeout: 5) else { return }
        albumsLink.tap()

        // Wait for album list to load
        let albumList = app.otherElements["cloud.albums.list"]
        guard albumList.waitForExistence(timeout: 10) else {
            XCTFail("Album list should load")
            return
        }

        // Tap the first album row
        let firstAlbum = app.cells.firstMatch
        guard firstAlbum.waitForExistence(timeout: 5) else {
            // No albums — server may be empty
            return
        }
        firstAlbum.tap()

        // Track list should appear
        let trackList = app.otherElements["cloud.albumDetail.trackList"]
        let header = app.otherElements["cloud.albumDetail.header"]
        let appeared = trackList.waitForExistence(timeout: 10) || header.waitForExistence(timeout: 10)
        XCTAssertTrue(appeared, "Album detail should show track list or header")
    }

    /// Verifies navigating Artists → tapping an artist → filtered albums appear.
    func testCategoryDrillDown() {
        app.launch()
        openCloudBrowser()

        let artistLink = app.buttons["cloud.browse.artist"]
        guard artistLink.waitForExistence(timeout: 5) else { return }
        artistLink.tap()

        // Wait for artist list to load
        let categoryList = app.otherElements["cloud.category.list"]
        guard categoryList.waitForExistence(timeout: 10) else {
            XCTFail("Artist category should load")
            return
        }

        // Tap the first artist
        let firstItem = app.cells.firstMatch
        guard firstItem.waitForExistence(timeout: 5) else { return }
        firstItem.tap()

        // Filtered album list or empty state should appear
        let filteredList = app.otherElements["cloud.filtered.list"]
        let emptyState = app.otherElements["cloud.filtered.empty"]
        let appeared = filteredList.waitForExistence(timeout: 10) || emptyState.waitForExistence(timeout: 10)
        XCTAssertTrue(appeared, "Category drill-down should show filtered albums or empty state")
    }

    // MARK: - Phase 3: CI-Reliable Offline Tests (mocked network)

    /// Launches app with mock network — verifies cloud browser renders with stubbed data.
    func testCloudBrowserWithMockData() {
        app.launchArguments += ["-MockNetwork"]
        app.launch()
        openCloudBrowser()

        // Stats should render from mock data (identifier propagates to StaticText children)
        let stats = app.staticTexts.matching(identifier: "cloud.stats").firstMatch
        XCTAssertTrue(stats.waitForExistence(timeout: 5), "Stats section should render with mock data")

        // Browse section categories should be visible
        let yearLink = app.buttons["cloud.browse.year"]
        XCTAssertTrue(yearLink.waitForExistence(timeout: 3), "Year category should be listed")

        // Tap Years — should load mock year data without error
        yearLink.tap()

        let errorText = app.staticTexts.matching(identifier: "cloud.category.error").firstMatch
        // Wait for category content to load
        sleep(2)
        XCTAssertFalse(errorText.exists, "Years should load without error from mock data")
    }

    /// Verifies that a malformed JSON response shows an error message to the user.
    func testDecodingErrorShowsMessage() {
        // The mock data is valid by default; to test decoding errors we'd need
        // a mechanism to inject bad data. For now, test with an unconfigured server
        // (no -MockNetwork, no real server) — the error path exercises the same UI.
        app.launch()

        // If server is NOT configured, opening cloud browser shows "Not Connected"
        openCloudBrowser()

        // Either "Not Connected" or the browse view should appear — no crash
        let notConnected = app.staticTexts["Not Connected"]
        let browseSection = app.buttons["cloud.browse.album"]
        let appeared = notConnected.waitForExistence(timeout: 5) || browseSection.waitForExistence(timeout: 5)
        XCTAssertTrue(appeared, "Should show either Not Connected or Browse section")
    }

    /// Verifies album tracks render correctly with mocked data.
    func testAlbumTracksWithMockData() {
        app.launchArguments += ["-MockNetwork"]
        app.launch()
        openCloudBrowser()

        // Navigate to Albums
        let albumsLink = app.buttons["cloud.browse.album"]
        guard albumsLink.waitForExistence(timeout: 5) else {
            XCTFail("Album category should be visible with mock data")
            return
        }
        albumsLink.tap()

        // Wait for mock album to appear (List renders as CollectionView on iOS)
        let firstAlbumRow = app.buttons["cloud.album.row.album-1"]
        guard firstAlbumRow.waitForExistence(timeout: 5) else {
            XCTFail("Album list should render with mock data")
            return
        }

        // Tap first album
        firstAlbumRow.tap()

        // Track list should appear (mock returns tracks with known titles)
        let trackTitle = app.staticTexts["First Track"]
        XCTAssertTrue(trackTitle.waitForExistence(timeout: 5), "Track list should render with mock data")
    }
}
