import XCTest

/// Reusable helper functions for UI tests
struct UITestHelpers {
    private static func loginUsernameField(app: XCUIApplication) -> XCUIElement {
        app.textFields[AccessibilityIdentifiers.Authentication.usernameField]
    }

    // MARK: - Authentication Helpers

    /// Logs out the user if they are currently logged in
    /// - Parameter app: The XCUIApplication instance
    static func logout(app: XCUIApplication) {
        // Already on login screen.
        let usernameField = loginUsernameField(app: app)
        if usernameField.waitForExistence(timeout: 2) {
            return
        }

        // In onboarding flow, navigate to login.
        let onboardingRoot = app.otherElements[UITestID.Root.onboarding]
        if onboardingRoot.waitForExistence(timeout: 2) {
            ensureOnLoginScreen(app: app)
            return
        }

        // Check if we have a tab bar (logged in state)
        let tabBar = app.tabBars.firstMatch
        guard tabBar.exists else { return }

        // Navigate to Residences tab first
        let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
        if residencesTab.exists {
            residencesTab.tap()
        }

        // Tap settings button
        let settingsButton = app.buttons[AccessibilityIdentifiers.Navigation.settingsButton]
        if settingsButton.waitForExistence(timeout: 3) && settingsButton.isHittable {
            settingsButton.tap()
        }

        // Find and tap logout button — the profile sheet uses a lazy
        // SwiftUI List so the button may not exist until scrolled into view
        let logoutButton = app.buttons[AccessibilityIdentifiers.Profile.logoutButton]

        if !logoutButton.waitForExistence(timeout: 3) {
            // Scroll down in the profile sheet's CollectionView
            let collectionView = app.collectionViews.firstMatch
            if collectionView.exists {
                for _ in 0..<5 {
                    collectionView.swipeUp()
                    if logoutButton.waitForExistence(timeout: 1) { break }
                }
            }
        }

        if logoutButton.waitForExistence(timeout: 3) {
            if logoutButton.isHittable {
                logoutButton.tap()
            } else {
                logoutButton.forceTap()
            }

            // Confirm logout in alert if present
            let alert = app.alerts.firstMatch
            if alert.waitForExistence(timeout: 3) {
                let confirmLogout = alert.buttons["Log Out"]
                if confirmLogout.waitForExistence(timeout: 2) {
                    confirmLogout.tap()
                }
            }
        }

        // Wait for the app to transition back to login screen after logout
        let loginRoot = app.otherElements[UITestID.Root.login]
        let loggedOut = usernameField.waitForExistence(timeout: 15)
            || loginRoot.waitForExistence(timeout: 5)

        if !loggedOut {
            // Check if we landed on onboarding instead (when onboarding state was reset)
            let onboardingRoot = app.otherElements[UITestID.Root.onboarding]
            if onboardingRoot.waitForExistence(timeout: 3) {
                return // Logout succeeded, landed on onboarding
            }
            XCTFail("Failed to log out - login username field should appear. App state:\n\(app.debugDescription)")
        }
    }

    /// Logs in a user with the provided credentials
    /// - Parameters:
    ///   - app: The XCUIApplication instance
    ///   - username: The username/email to use for login
    ///   - password: The password to use for login
    static func login(app: XCUIApplication, username: String, password: String) {
        // Find username field by accessibility identifier
        let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.usernameField]
        XCTAssertTrue(usernameField.waitForExistence(timeout: 5), "Username field should exist")
        usernameField.focusAndType(username, app: app)

        // Find password field - it could be TextField (if visible) or SecureField
        var passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.passwordField]
        if !passwordField.exists {
            passwordField = app.textFields[AccessibilityIdentifiers.Authentication.passwordField]
        }
        XCTAssertTrue(passwordField.waitForExistence(timeout: 3), "Password field should exist")
        passwordField.focusAndType(password, app: app)

        // Find and tap login button
        let loginButton = app.buttons[AccessibilityIdentifiers.Authentication.loginButton]
        XCTAssertTrue(loginButton.waitForExistence(timeout: 3), "Login button should exist")
        loginButton.tap()

        // Wait for login to complete, handling verification gate if shown
        let tabBarAfterLogin = app.tabBars.firstMatch
        let verificationCodeField = app.textFields[AccessibilityIdentifiers.Authentication.verificationCodeField]
        _ = tabBarAfterLogin.waitForExistence(timeout: 15)
            || verificationCodeField.waitForExistence(timeout: 3)

        let onboardingCodeField = app.textFields[AccessibilityIdentifiers.Onboarding.verificationCodeField]
        if verificationCodeField.waitForExistence(timeout: 3) || onboardingCodeField.waitForExistence(timeout: 2) {
            let codeField = verificationCodeField.exists ? verificationCodeField : onboardingCodeField
            codeField.focusAndType("123456", app: app)
            let verifyButton = app.buttons[AccessibilityIdentifiers.Authentication.verifyButton].exists
                ? app.buttons[AccessibilityIdentifiers.Authentication.verifyButton]
                : app.buttons[AccessibilityIdentifiers.Onboarding.verifyButton]
            if verifyButton.exists {
                verifyButton.tap()
            } else {
                app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Verify'")).firstMatch.tap()
            }
            _ = tabBarAfterLogin.waitForExistence(timeout: 15)
        }
    }

    /// Ensures the user is logged out before running a test
    /// - Parameter app: The XCUIApplication instance
    static func ensureLoggedOut(app: XCUIApplication) {
        logout(app: app)
        ensureOnLoginScreen(app: app)
    }

    /// Ensures the user is logged in with test credentials before running a test
    /// - Parameter app: The XCUIApplication instance
    /// - Parameter username: Optional username (defaults to "testuser")
    /// - Parameter password: Optional password (defaults to "TestPass123!")
    static func ensureLoggedIn(app: XCUIApplication, username: String = "testuser", password: String = "TestPass123!") {
        // Check if already logged in (tab bar visible)
        let tabBar = app.tabBars.firstMatch
        if tabBar.exists {
            return // Already logged in
        }

        ensureOnLoginScreen(app: app)

        let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.usernameField]
        if usernameField.waitForExistence(timeout: 5) {
            login(app: app, username: username, password: password)

            // Wait for main screen to appear
            _ = tabBar.waitForExistence(timeout: 10)
        }
    }

    static func ensureOnLoginScreen(app: XCUIApplication) {
        let usernameField = loginUsernameField(app: app)
        if usernameField.waitForExistence(timeout: 5) {
            return
        }

        // Handle persisted authenticated sessions first.
        let mainTabsRoot = app.otherElements[UITestID.Root.mainTabs]
        if mainTabsRoot.exists || app.tabBars.firstMatch.exists {
            logout(app: app)
            if usernameField.waitForExistence(timeout: 10) {
                return
            }
        }

        // Wait for a stable root state before interacting.
        let loginRoot = app.otherElements[UITestID.Root.login]
        let onboardingRoot = app.otherElements[UITestID.Root.onboarding]

        // Check for standalone login screen first (when --complete-onboarding is active)
        if loginRoot.waitForExistence(timeout: 8) {
            _ = usernameField.waitForExistence(timeout: 10)
        } else if onboardingRoot.waitForExistence(timeout: 5) {
            // Handle both pure onboarding and onboarding + login sheet.
            let onboardingLoginButton = app.buttons[AccessibilityIdentifiers.Onboarding.loginButton]
            if onboardingLoginButton.waitForExistence(timeout: 5) {
                if onboardingLoginButton.isHittable {
                    onboardingLoginButton.tap()
                } else {
                    onboardingLoginButton.forceTap()
                }
            } else {
                let welcome = OnboardingWelcomeScreen(app: app)
                welcome.waitForLoad()
                welcome.tapAlreadyHaveAccount()
            }
        }

        XCTAssertTrue(
            usernameField.waitForExistence(timeout: 20),
            "Expected to reach login screen from current app state"
        )
    }
}
